I'm learning Xamarin, and I'm trying to understand how can I pass a value from my class file to a .xmal file for display? Is that data binding or something else.
Example:
My file.cs
namespace MyApp.Views
{
public partial class LandingPage : ContentPage
{
public LandingPage()
{
string myvalue = "hello world";
}
}
}
file.xaml
<StackLayout>
<Label Text="myvalue" />
</StackLayout>
I want the "myvalue" to pass from my class to my xaml file.
yes, data binding is the answer
<Label Text="{Binding MyValue}" />
you can only bind to public properties
public string MyValue { get; set; }
public LandingPage()
{
InitializeComponent();
MyValue = "hello world";
this.BindingContext = this;
}
Related
Oke here is the context: I have a Xamarin Application that connects to ASP.NET rest service. Currently I am working on the databinding on my views
There is a certain Data model called prestatie which has a foreingn Key Reference to the Trainer model class and another that foreign key reference to the Getuigschrift Class.
public class Prestatie : INotifyPropertyChanged
{
[Key]
private Guid _PrestatieID;
public Guid PrestatieID
{
get => _PrestatieID;
set
{
_PrestatieID = value;
RaisePropertyChanged(nameof(PrestatieID));
}
}
private string _Rekeningnummer;
public string Rekeningnummer
{
get => _Rekeningnummer;
set
{
_Rekeningnummer = value;
RaisePropertyChanged(nameof(Rekeningnummer));
}
}
private string _Rijksregisternummer;
public string Rijksregisternummer
{
get => _Rijksregisternummer;
set
{
_Rijksregisternummer = value;
RaisePropertyChanged(nameof(Rijksregisternummer));
}
}
[ForeignKeyAttribute("Trainer")]
public Guid? TrainerID
{
get;
set;
}
public Trainer Trainer
{
get;
set;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Trainer Class:
public class Trainer : Persoon
{
private Guid _TrainerID;
public Guid TrainerID
{
get => _TrainerID;
set
{
_TrainerID = value;
RaisePropertyChanged(nameof(TrainerID));
}
}
public ICollection<Prestatie> Prestaties
{
get;
set;
}
public Getuigschrift Getuigschriften
{
get;
set;
}
private Guid _GetuigschriftID;
public Guid? GetuigschriftID
{
get => _GetuigschriftID;
set
{
_GetuigschriftID = (Guid)value;
RaisePropertyChanged(nameof(GetuigschriftID));
}
}
}
Now I got two ViewModels setup fto represent this data, one for an overview and the second for the Details/Editing/adding. Databinding of items is on the viewmodels, so in Theory I should make these relations up in my view models but I am uncertain on how to do this. At one hand I just need to have some labels back in the ItemViewModel, and the other hand I need sort of comobox/list/picker for the data input to just get the Foregin Key.
Solutions I have tried it something like this, but that does not seem to work.
Xamarin ListView MVVM DataBinding
Here is a small snippet of my viewmodels, I cant post more because of the character limit.
public class PrestatieViewModel : BaseViewModel
{
private ObservableCollection<Prestatie> _prestaties;
private readonly IPrestatieDataService _prestatieDataService;
private readonly INavigationService _navigationService;
public ObservableCollection<Prestatie> Prestaties
{
get => _prestaties;
set
{
_prestaties = value;
OnPropertyChanged("Prestaties");
}
}
public class PrestatieDetailViewModel : BaseViewModel
{
private Prestatie _selectedPrestatie;
private readonly IPrestatieDataService _prestatieDataService;
private readonly INavigationService _navigationService;
public Prestatie SelectedPrestatie
{
get => _selectedPrestatie;
set
{
_selectedPrestatie = value;
OnPropertyChanged(nameof(SelectedPrestatie));
}
}
You can bind the picker's selectedItem with trainer so you could pick and set the value.
Here are the code you could refer to
xmal:
<ContentPage.BindingContext>
<local:PeopleViewModel/>
</ContentPage.BindingContext>
<CollectionView x:Name="mycol">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="100,* "
RowDefinitions="*,*">
<Label Text="{Binding Name}" BackgroundColor="LightBlue"/>
<Picker x:Name="mypicker" Grid.Column="1" Title="Select a Trainer"
TitleColor="Red"
ItemsSource="{Binding pgs}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding Trainer}">
</Picker>
<Label Text="Trainer:" Grid.Row="1" Grid.Column="0" BackgroundColor="AliceBlue"/>
<Label
Grid.Row="1"
Grid.Column="1"
Text="{Binding Trainer.Name}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
viewmodel:
public class PeopleViewModel:INotifyPropertyChanged
{
string name;
People trainer;
public List<People> pgs { get; private set; } = new List<People>{new People{Name="Trainer1" },
new People{ Name="Trainer2"} ,
new People{ Name="Trainer3"} };
public string Name {
get { return name; }
set { if (name != value)
{ name = value;
OnPropertyChanged();
}
} }
public People Trainer
{
get { return trainer; }
set
{
if (trainer != value)
{
trainer = value;
OnPropertyChanged();
}
}
}
#region INotifyPropertyChanged
void OnPropertyChanged(string name=null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
}}
I have:
public class ViewCustomerViewModel
{
public Customer CustomerInfo { get; set; }
public static string baseUrl = "https://xxxx/Customers/";
public ViewCustomerViewModel()
{
CheckUserinfo();
}
public async void CheckUserinfo()
{
.....
var url = baseUrl;
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", xxx);
string jsonStr = await client.GetStringAsync(url);
var res = JsonConvert.DeserializeObject<Customer>(jsonStr);
CustomerInfo = res;
}
}
CustomerInfo returns oke
I have PageOne.xaml
<ContentPage ...>
<ContentPage.BindingContext>
<locals:ViewCustomerViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<Label Text={Binding xxxx} />
</ContentPage.Content>
</ContentPage>
PageOne.xaml.cs
This is how I get the data to Binding:
public Customer CustomerInfo { get; set; }
public PageOne()
{
InitializeComponent();
BindingContext = new ViewCustomerViewModel();
}
However: in PageOne.xaml page when I <Label Text={Binding xxxx} /> ---> Binding xxxx, I get no value of Customer class.
I was doing something wrong. Please help me how can I display CustomerInfo results. Thank you
Update
PageOne.xaml
<ContentPage ...>
<ContentPage.Content>
<Label Text={Binding xxxx} />
</ContentPage.Content>
</ContentPage>
The problem is that when the constructor completes, the instance is still being asynchronously initialized, and there isn’t an obvious way to determine when the asynchronous initialization has completed.
Instead of calling from the constructor, do it from the page's OnAppearing method, which you can make async.
Or you could change your viewmodel,make a Factory Pattern for it.From the link,there are several methods you could refer to.
I have a carouselview in my Xam.Forms project. I have also created 3 ContentViews (one for each DataTemplate). My template selector class looks like this
public class DashboardTemplateSelector : DataTemplateSelector
{
public DataTemplate QuickMessageTemplate { get; set; }
public DataTemplate DataViewTemplate { get; set; }
public DataTemplate LastUsedTemplate { get; set; }
public DashboardTemplateSelector()
{
QuickMessageTemplate = new DataTemplate(typeof(QuickMessage));
DataViewTemplate = new DataTemplate(typeof(DataView));
LastUsedTemplate = new DataTemplate(typeof(LastusedView));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var cv = (Frame)item;
DataTemplate rv = null;
switch(cv.ClassId)
{
case "data":
rv = DataViewTemplate;
break;
case "quick":
rv= QuickMessageTemplate;
break;
case "last":
rv = LastUsedTemplate;
break;
}
return rv;
}
Nothing out of the ordinary and I have the ClassId on each frame within the ContentView set to match the name in the switch.
When I build the app and run it, it looks fine but there is nothing in the CarouselView and a break point set in the OnSelectTemplate method (the first line) is never hit.
My XAML for the carouselview is this
<ContentPage.Resources>
<ResourceDictionary>
<local:DashboardTemplateSelector x:Key="templateSelector" />
</ResourceDictionary>
</ContentPage.Resources>
<CarouselView Grid.Row="2" PeekAreaInsets="12" Margin="8" ItemTemplate="{StaticResource templateSelector}" HeightRequest="200" BackgroundColor="BlueViolet" />
The view shows (can see the background colour) but nothing in the view itself.
I've only checked this on a physical android device and not on iOS, but I'm guessing the same retult. My guess is that I can't cast to a Frame for the object, but I'm not sure.
To populate data, you have to set an ItemsSource via DataBinding or Code-Behind.
Then your DataTemplateSelector will be hit with each item of the ItemsSource as object item. Please see the documentation here: https://learn.microsoft.com/fr-fr/xamarin/xamarin-forms/user-interface/carouselview/layout
<CarouselView Grid.Row="2" PeekAreaInsets="12" Margin="8" ItemsSource="{Binding ViewsViewModels} ItemTemplate="{StaticResource templateSelector}" HeightRequest="200" BackgroundColor="BlueViolet" />
The item in OnSelectTemplate is itemsource data. Change the item to container and use CarouselView instead of Frame. Do not forget to set the ClassId of your CarouselView.
The whold project for your reference.
Xaml:
<ContentPage.Resources>
<ResourceDictionary>
<local:DashboardTemplateSelector x:Key="templateSelector" />
</ResourceDictionary>
</ContentPage.Resources>
<CarouselView
Grid.Row="2"
Margin="8"
ClassId="data"
HeightRequest="200"
ItemTemplate="{StaticResource templateSelector}"
ItemsSource="{Binding infos}"
PeekAreaInsets="12" />
Code behind:
public partial class MainPage : ContentPage
{
public ObservableCollection<Info> infos { get; set; }
public MainPage()
{
InitializeComponent();
infos = new ObservableCollection<Info>()
{
new Info{ DataViewText="DataViewText1", LastusedViewText="LastusedViewText1", QuickMessageText="QuickMessageText1"},
new Info{ DataViewText="DataViewText2", LastusedViewText="LastusedViewText2", QuickMessageText="QuickMessageText2"},
new Info{ DataViewText="DataViewText3", LastusedViewText="LastusedViewText3", QuickMessageText="QuickMessageText3"},
new Info{ DataViewText="DataViewText4", LastusedViewText="LastusedViewText4", QuickMessageText="QuickMessageText4"},
};
this.BindingContext = this;
}
}
public class Info
{
public string QuickMessageText { get; set; }
public string DataViewText { get; set; }
public string LastusedViewText { get; set; }
}
QuickMessage, DataView and LastusedView is a contentview with label which binding a text.
QuickMessage:
<Label Text="{Binding QuickMessageText}" />
DataView:
<Label Text="{Binding DataViewText}" />
LastusedView:
<Label Text="{Binding LastusedViewText}" />
DashboardTemplateSelector:
public class DashboardTemplateSelector : DataTemplateSelector
{
public DataTemplate QuickMessageTemplate { get; set; }
public DataTemplate DataViewTemplate { get; set; }
public DataTemplate LastUsedTemplate { get; set; }
public DashboardTemplateSelector()
{
QuickMessageTemplate = new DataTemplate(typeof(QuickMessage));
DataViewTemplate = new DataTemplate(typeof(DataView));
LastUsedTemplate = new DataTemplate(typeof(LastusedView));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var cv = (CarouselView)container;
DataTemplate rv = null;
switch (cv.ClassId)
{
case "data":
rv = DataViewTemplate;
break;
case "quick":
rv = QuickMessageTemplate;
break;
case "last":
rv = LastUsedTemplate;
break;
}
return rv;
}
}
I am learning MVVM in Xamarin forms using Prism. I have implemented a login functionality which uses a User model class. But the bindings are not working. Please review the code and suggest corrections.
I am not sure how to bind the control's text property to the Model class object's properties.
LoginPage.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:prism="http://prismlibrary.com"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="LeaveTracker.Views.LoginPage"
Title="{Binding Title}">
<StackLayout Orientation="Vertical" BindingContext="{Binding UserObj}">
<Entry Placeholder="User ID" Text="{Binding UserID}"/>
<Entry Placeholder="Password" Text="{Binding Password}" IsPassword="True"/>
</StackLayout>
</ContentPage>
LoginPageViewModel.cs
public class LoginPageViewModel : ViewModelBase
{
private User _user;
private IFirebaseService _firebaseService;
public User UserObj
{
get { return _user; }
set { SetProperty(ref _user, value); }
}
public DelegateCommand LoginCommand { get; set; }
public LoginPageViewModel(IFirebaseService firebaseService, INavigationService navigationService) : base(navigationService)
{
Title = "Log In";
_firebaseService = firebaseService;
LoginCommand = new DelegateCommand(Login, CanLogin);
}
private void Login()
{
var x = _firebaseService.LoginAsync(_user);
}
private bool CanLogin()
{
if (string.IsNullOrEmpty(_user.UserID) && string.IsNullOrEmpty(_user.Password))
{
return true;
}
return false;
}
User.cs
public class User
{
private string _userID;
public string UserID
{
get { return _userID; }
set { _userID = value; }
}
private string _password;
public string Password
{
get { return _password; }
set { _password = value; }
}
}
your BindingContext is LoginPageViewModel, and UserObj is a property of the VM, so your binding path needs to include UserObj
<Entry Placeholder="User ID" Text="{Binding UserObj.UserID}"/>
<Entry Placeholder="Password" Text="{Binding UserObj.Password}" IsPassword="True"/>
I have an api URL here which provides the response below.
Json
{"status":200,"message":"Operation done successfully","data":{"enableNext":false,"products":[{"image":"http://bresa.lazyhost.in/upload/product/1/Tjtqr8.jpg","id":1,"code":"PROi6v8X5261","name":"Spandex Stretch Lounge Sofa with Couch Seat Cover SlipCover","description":"nice sofa , very comfortable for sitting in Living room","tags":"chairs, sofa, ","price":"1000.00","quantity":100,"images":["http://bresa.lazyhost.in/upload/product/1/Tjtqr8.jpg"]}]}}
The model class for above response is below.
CarouselModel
namespace CameliaMaison.Models
{
public partial class CarouselModel
{
[JsonProperty("status")]
public long Status { get; set; }
[JsonProperty("message")]
public string Message { get; set; }
[JsonProperty("data")]
public List<CarouselData> Carouseldata { get; set; }
}
public partial class CarouselData
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("path")]
public string Path { get; set; }
}
}
The ViewModel
namespace CameliaMaison.ViewModels
{
public class CarouselImagesViewModel
{
private List<CarouselData> items;
public List<CarouselData> Items
{
get { return items; }
set
{
items = value;
}
}
public CarouselImagesViewModel()
{
var responseObj = MyHTTP.GetApiData().Result;
foreach(CarouselData item in responseObj){
Items.Add(item);
}
}
}
public class MyHTTP
{
public static async Task<List<CarouselData>> GetApiData()
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync("http://bresa.lazyhost.in/api/banners");
CarouselModel categoriesData = new CarouselModel();
var content = await response.Content.ReadAsStringAsync();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
categoriesData = JsonConvert.DeserializeObject<CarouselModel>(await response.Content.ReadAsStringAsync());
}
return categoriesData.Carouseldata;
}
}
}
I need to parse the JSON and store in Model Object and populate the data in the listview via MVVM.However, there seems to be something wrong with the implementation. I am unable to figure out and the content page class is below.
ProductsListPage.xaml.cs
public partial class ProductsListPage : ContentPage
{
public ProductsListPage()
{
InitializeComponent();
}
}
ProductsListPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:constants="clr-namespace:CameliaMaison;assembly=CameliaMaison"
x:Class="CameliaMaison.Views.ProductsListPage"
Title="Employee List">
<ListView x:Name="ListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="ABC" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
First you must have a button on your view page.
<Button Command = "{Binding GetInfoCommand}" />
Than you must create a view-model. Something like this :
public class CarouselModel: INotifyPropertyChanged
{
//get a reference from the another class where you make the call to get the json
Tasks ts = new Tasks();
List<CarouselData> _carouseldata ;
public List<CarouselData> CarouselData
{
get { return _carouseldata ; }
set
{
if (value == _carouseldata ) return;
_carouseldata = value;
OnPropertyChanged();
}
}
public ICommand GetIInfoComand
{
get
{
return new Command(async()=>
{
CarouselData= await _apiServices.GetInfo();
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Than in your tasks.cs class you must consume the call where you get the json. This simple. :)
==edit==
In your view you must replace your textcell.Something like this :
<ListView x:Name="ListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name }" />
</DataTemplate>
</ListView.ItemTemplate>
Also you get a reference of your view model and place it before the listview :
<ContentPage.BindingContext>
<CarouselModel/>
</ContentPage.BindingContext>