I'm trying to make a simple list application in xamarin forms using data binding but the data doesn't appear.
Here's my MainPage.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:Practice3"
x:Class="Practice3.MainPage">
<StackLayout>
<ListView ItemsSource="{Binding Films}">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell
Text="{Binding Title}"
TextColor="Black"
Detail="{Binding Resume}"
DetailColor="Gray"
/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
Here's MainPage.xaml.cs
namespace Practice3
{
public partial class MainPage : ContentPage
{
public ObservableCollection<Film> Films { get; set; }
public MainPage()
{
InitializeComponent();
Films = new ObservableCollection<Film>{
new Film{
Title = "X-Men (2000)",
Resume = "Two mutants come to a private academy for their kind whose resident superhero team must oppose a terrorist organization with similar powers."
},
new Film{
Title = "X-Men 2 (2003)",
Resume = "The X-Men band together to find a mutant assassin who has made an attempt on the President's life, while the Mutant Academy is attacked by military forces."
},
new Film{
Title = "X-Men: The last stand (2006)",
Resume = "When a cure is found to treat mutations, lines are drawn amongst the X-Men, led by Professor Charles Xavier, and the Brotherhood, a band of powerful mutants organized under Xavier's former ally, Magneto."
}
};
}
}
}
And Films.cs
namespace Practice3
{
public class Film
{
public string Title { get; set; }
public string Resume { get; set; }
}
}
I tried to just put a Label in the StackLayout and it appeared clearly, but the ListView seems to be empty or invisible.
I finally solved it this way:
MainPage.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:Practice3"
x:Class="Practice3.MainPage">
<StackLayout>
<ListView x:Name="FilmList">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell
Text="{Binding Title}"
TextColor="Black"
Detail="{Binding Resume}"
DetailColor="Gray"
/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
And MainPage.xaml.cs
namespace Practice3
{
public partial class MainPage : ContentPage
{
public ObservableCollection<Film> Films { get; set; }
public MainPage()
{
InitializeComponent();
Films = new ObservableCollection<Film>{
new Film{
Title = "X-Men (2000)",
Resume = "Two mutants come to a private academy for their kind whose resident superhero team must oppose a terrorist organization with similar powers."
},
new Film{
Title = "X-Men 2 (2003)",
Resume = "The X-Men band together to find a mutant assassin who has made an attempt on the President's life, while the Mutant Academy is attacked by military forces."
},
new Film{
Title = "X-Men: The last stand (2006)",
Resume = "When a cure is found to treat mutations, lines are drawn amongst the X-Men, led by Professor Charles Xavier, and the Brotherhood, a band of powerful mutants organized under Xavier's former ally, Magneto."
}
};
FilmList.ItemsSource = Films;
}
}
}
Related
I have two pages. On the first I want to see every elements from ObservableCollection and the second I want to add new element to ObservableCollection. When I give breakpoint I see that the ObservableCollection has new element, but when I back to the first page new element dosen't exist. What should I do?
There is code
public ListViewFlascardViewModel()
{
GoToAddFlashcard = new Command(goToAddFlashcard);
Fiszka = new ObservableCollection<Flashcard>();
Fiszka.Add(new Flashcard {Name = "hello" });
}
public Command GoToAddFlashcard { get; }
Flashcard flashcard = new Flashcard();
async void goToAddFlashcard()
{
await Shell.Current.GoToAsync(nameof(View.NoweFiszki));;
}
public ObservableCollection<Flashcard> Fiszka { get; set; }
}
And there is second page:
class NoweFiszkiViewModel
{
public Command SaveFlashcard { get; }
public NoweFiszkiViewModel()
{
SaveFlashcard = new Command(save);
}
ListViewFlascardViewModel list = new ListViewFlascardViewModel();
private async void save()
{
list.Fiszka.Add(new Flashcard { Name = "Bye" });
await Shell.Current.GoToAsync("..");
}
}
I try a lot of things, but nothing help. I am new in C# and I will be appreciated for every help.
I add whole code.
There is view
ListViewFlashcard
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MVVM.ViewModel"
xmlns:model="clr-namespace:MVVM.Model"
x:Class="MVVM.View.ListViewFlashcard"
x:DataType="viewmodels:ListViewFlascardViewModel"
>
<ContentPage.BindingContext>
<viewmodels:ListViewFlascardViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Button
Command="{Binding GoToAddFlashcard}"
Text="Click Me"/>
<ListView
ItemsSource="{Binding Fiszka}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:Flashcard">
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
NoweFiszki
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MVVM.ViewModel"
x:Class="MVVM.View.NoweFiszki">
<ContentPage.BindingContext>
<viewmodels:NoweFiszkiViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Entry Text="{Binding Text, Mode=TwoWay}"/>
<Button Text="Save"
Command="{Binding SaveFlashcard}"/>
</StackLayout>
</ContentPage.Content>
And Model
Flashcard
public class Flashcard
{
public string Name { get; set; }
}
And AppShell
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MVVM.AppShell"
xmlns:local="clr-namespace:MVVM.View"
>
<FlyoutItem Title="Add Flashcard" Icon="icon_about.png">
<ShellContent ContentTemplate="{DataTemplate local:ListViewFlashcard}"/>
</FlyoutItem>
public partial class AppShell : Xamarin.Forms.Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(NoweFiszki), typeof(NoweFiszki));
}
}
It is everything what I wrote.
A couple of things have to be corrected on your code:
In NoweFiszkiViewModel you are creating a new instance of ListViewFlascardViewModel every time, and adding elements on that new instance, which does not affect the instance to which ListViewFlashcard is actually bound.
To fix this, you will have to create a public static ListViewFlascardViewModel in ListViewFlashcard.xaml.cs and set the binding context to it, as follows
ListViewFlashcard.xaml.cs
public static ListViewFlascardViewModel vm { get; set; }
public ListViewFlashcard()
{
InitializeComponent();
vm = new ListViewFlascardViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = vm;
}
then correct ListViewFlashcard.xaml as follows
ListViewFlashcard.xaml
<!--<ContentPage.BindingContext>
<viewmodels:ListViewFlascardViewModel/>
</ContentPage.BindingContext>-->
<StackLayout>
<Button
Command="{Binding GoToAddFlashcard}"
Text="Click Me"/>
<ListView ItemsSource="{Binding Fiszka}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewmodels:Flashcard">
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Finally you have to correct NoweFiszkiViewModel.cs as follows, adding the new elements to the public static view model:
NoweFiszkiViewModel.cs
public Command SaveFlashcard { get; }
public NoweFiszkiViewModel()
{
SaveFlashcard = new Command(save);
}
//ListViewFlascardViewModel list = new ListViewFlascardViewModel();
private async void save()
{
ListViewFlashcard.vm.Fiszka.Add(new Flashcard { Name = "Bye" });
await Shell.Current.GoToAsync("..");
}
My MainPage.xaml page is bound to ClientsViewModel.cs. This page has a ListView bound to an ObservableCollection property.
The NewClient.xaml page and entry fields are also bound to the ClientsViewModel.cs.
When I save a new client using the NewClient.xaml form and navigate back to MainPage.xaml (using the navigation back arrow) I expect to see the newly added client in the MainPage.xaml ListView however I do not see this change.
How come the ListView in MainPage.xaml isn't showing the newly updated record? Where am I going wrong?
It may be worthwhile mentioning that my actual project will be using SQLite, so the ObseravbleCollection will eventually be obtaining records directly from an SQLite database, so any help or advice around this would be greatly appreciated also.
Refer below code, or clone from my GitHub repository https://github.com/minlopalis/XamarinForms-ListView-DataBinding.git
(Model) Client.cs
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
}
(ViewModel) BaseViewModel.cs
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
(View Model) ClientViewModel.cs
public class ClientViewModel : BaseViewModel
{
private ObservableCollection<Client> clients;
public ObservableCollection<Client> Clients
{
get { return clients; }
set
{
clients = value;
OnPropertyChanged();
}
}
public Command SaveClientCommand { get; }
public ClientViewModel()
{
this.Clients = new ObservableCollection<Client>();
SaveClientCommand = new Command(()=> {
Client client = new Client()
{
Name = Name,
Phone = Phone
};
Clients.Add(client);
OnPropertyChanged(nameof(Clients));
});
}
private int id;
public int Id
{
get { return id; }
set
{
id = value;
OnPropertyChanged();
}
}
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged();
}
}
private string phone;
public string Phone
{
get { return phone; }
set
{
phone = value;
OnPropertyChanged();
}
}
}
(View) MainPage.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:viewModels="clr-namespace:DataBinding.ViewModels"
x:Class="DataBinding.MainPage">
<ContentPage.BindingContext>
<viewModels:ClientViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Label Text="Client List"></Label>
<ListView ItemsSource="{Binding Clients}">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Phone}"/>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Add Client"
Clicked="AddClientButton_Clicked"/>
</StackLayout>
</ContentPage>
(View) NewClient.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:viewModels="clr-namespace:DataBinding.ViewModels"
x:Class="DataBinding.Views.NewClient">
<ContentPage.BindingContext>
<viewModels:ClientViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Label Text="Add New Client" />
<Label Text="Name"/>
<Entry Text="{Binding Name}"/>
<Label Text="Phone"/>
<Entry Text="{Binding Phone}"/>
<Button Text="Save"
Command="{Binding SaveClientCommand}"/>
<!-- Added ListView -->
<ListView ItemsSource="{Binding Clients}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Phone}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
I've downloaded your code from the repo and I think there is one big flaw in it causing this. You're setting your BindingContext in XAML on both pages. If you set a breakpoint in the constructor of the ClientViewModel, you will notice it gets called twice: once when the app boots, once when you click "Add Client".
This means you are looking at two separate instances of this class so your Client is in the wrong instance. You want to make sure that you are looking at the same view model.
Even more so, you might even want to make the separation of concerns even better by creating an extra, i.e.: CreateClientViewModel which is only responsible for creating the client and returning that object to the ClientViewModel which then in its turn adds that to the collection.
Hope this helps!
According to your description, you want to pass data when navigate between pages, I suggest you can use MessagingCenter.
MainPage:
<StackLayout>
<Label Text="Client List" />
<ListView ItemsSource="{Binding Clients}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}" />
<Label Text="{Binding Phone}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Command="{Binding SaveClientCommand}" Text="Add Client" />
</StackLayout>
public partial class Page9 : ContentPage
{
private ClientViewModel _clientmodel;
public ClientViewModel clientmodel
{
get { return _clientmodel; }
set
{
_clientmodel = value;
}
}
public Page9()
{
InitializeComponent();
this.BindingContext = new ClientViewModel(this.Navigation);
}
}
public class ClientViewModel
{
public ObservableCollection<Client> Clients { get; set; }
public Command SaveClientCommand { get; }
private INavigation _navigation;
public ClientViewModel(INavigation navitation)
{
Clients = new ObservableCollection<Client>();
Clients.Add(new Client() { Name = "client1", Phone = "123" });
_navigation = navitation;
SaveClientCommand = new Command(async() => {
await _navigation.PushAsync(new NewClient());
});
MessagingCenter.Subscribe<string, string[]>("test", "Add", (sender, values) =>
{
Client client = new Client() { Name=values[0],Phone=values[1]};
Clients.Add(client);
});
}
}
NewClient.xaml:
public partial class NewClient : ContentPage
{
public NewClient()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
string name = entry1.Text;
string phone = entry2.Text;
string[] values = { name,phone};
MessagingCenter.Send<string, string[]>("test", "Add", values);
Navigation.PopAsync();
}
}
By the way, you don't need to call PropertyChanged for ObservableCollection, because ObservableCollection Class Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
Thanks for everyone's help, I have solved my issue.
There were two problems with my code.
1. Two ViewModel Instances
As pointed out by Gerald Versluis I had two instances of my ViewModel. I fixed this issue by creating an instance of my view model in Application.Resources in my App.xaml page.
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBinding.App"
xmlns:ClientViewModel="clr-namespace:DataBinding.ViewModels">
<Application.Resources>
<ClientViewModel:ClientViewModel x:Key="ClientViewModel" />
</Application.Resources>
</Application>
And binding each page to the Static Resource (as 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:ViewModels="clr-namespace:DataBinding.ViewModels"
x:Class="DataBinding.Views.NewClient">
<ContentPage.BindingContext>
<StaticResource Key="ClientViewModel"/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Label Text="Add New Client" />
<Label Text="Name"/>
<Entry Text="{Binding Name}"/>
<Label Text="Phone"/>
<Entry Text="{Binding Phone}"/>
<Button Text="Save"
Command="{Binding SaveClientCommand}"/>
<!-- Added ListView -->
<ListView ItemsSource="{Binding ClientList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Name}"/>
<Label Text="{Binding Phone}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Thanks Gerald Versluis for your help. Check out his YouTube channel here.
2. Missing ViewCell
My MainPage.xaml was missing a ViewCell in the ListView. This was a simple typing oversight but was throwing a "'Specified cast is not valid" error. Big thanks to Alexander Fauland for his reply to this thread which helped me solve my missing ViewCell problem.
I already tried, string to ImageSource in my model but still I did not get image to display in ImageCell.
If I try this line of code <Image Source="https://img.icons8.com/carbon-copy/2x/user.png"></Image>, image displays fine.
Model
public class Contact
{
public string Name { get; set; }
public string Status { get; set; }
public ImageSource ImageUri { get; set; }
}
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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="App1.ListPage">
<ContentPage.Content>
<StackLayout>
<ListView x:Name="listView" SeparatorVisibility="Default" SeparatorColor="Blue">
<ListView.ItemTemplate>
<DataTemplate>
<!--<TextCell Text="{Binding Name}" Detail="{Binding Status}" TextColor="Accent" DetailColor="Beige"></TextCell>-->
<!--<TextCell Text="{Binding Name}" Detail="{Binding Status}"></TextCell>-->
`<ImageCell Text="{Binding Name}" Detail="{Binding Status}" ImageSource="{Binding ImageUri}`"></ImageCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!--<Image Source="https://img.icons8.com/carbon-copy/2x/user.png"></Image>-->
</StackLayout>
</ContentPage.Content>
</ContentPage>
XAML.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ListPage : ContentPage
{
public ListPage()
{
InitializeComponent();
listView.ItemsSource = new List<Contact>
{
new Contact{ Name="Monkey D. Luffy", ImageUri=ImageSource.FromUri(new Uri("https://img.icons8.com/carbon-copy/2x/user.png"))},
new Contact{ Name="Monkey D. Dragon",
ImageUri=ImageSource.FromUri(new Uri("https://img.icons8.com/carbon-copy/2x/user.png"))},
new Contact{ Name="Portgas D. Ace",
ImageUri=ImageSource.FromUri(new Uri("https://img.icons8.com/carbon-copy/2x/user.png")), Status="Hello World!"}
};
}
}
just did two tests on my playground.
I kicked off with a blank Xamarin.Forms project (only one MainPage there) in VS2019, then
updated the XF nuget package to 4.7.0.1142.
pasted your code.
remove two extra ` characters in your xaml code snippet.
It's working.
Then, I tried the string property in the model class like this:
public string ImageUri { get; set; }
And also set string value in xaml.cs class like this:
new Contact{ Name="", ImageUri="https://xxx.png"}
It's also working.
Not sure about the issue, but your code is fine anyway.
Try to use this nuget https://github.com/luberda-molinet/FFImageLoading for images. Is the best !!
I am attempting to use CarouselView for the first time and is kind of struck with it. I have XAML defined as follows
<?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"
xmlns:cv="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
x:Class="cSixty.Xam.Views.MainPage"
Title="MainPage">
<ContentPage.Content>
<StackLayout>
<Label Text="Name" HorizontalOptions="Center" VerticalOptions="CenterAndExpand"/>
<cv:CarouselView ItemsSource="{Binding Zoos}">
<cv:CarouselView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}" />
</DataTemplate>
</cv:CarouselView.ItemTemplate>
</cv:CarouselView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
The ViewModel has a Collection defined as follows.
public MainPageViewModel()
{
Zoos = new ObservableCollection<Zoo>
{
new Zoo
{
Name = "Woodland Park Zoo"
},
new Zoo
{
Name = "Cleveland Zoo"
},
new Zoo
{
Name = "Phoenix Zoo"
}
};
}
Where Zoo is defined as
public class Zoo
{
public string Name { get; set; }
}
My issue is that the Carousel just doesn't show up. Its all empty. Could someone guide me ?
Finally got it working, just thought will share it in case someone else runs into same situation.
You need install the Nuget Package in your Droid Project (or whatever platform you are using) as well. Previously, I had installed in my Portable Library only.
It looks like this
I've been working on a medical app and used MasterDetailPage to put all the navigation into one place. But it was too much so I figure to have it categorize into one of each. I thought of drop down menu on each page item. I've been searching the internet about implementing it but I couldn't figure it out.This is Xamarin on android and I wanted to make a drop down similar to the image above.
This is my code so far on C#
namespace MasterDetailPageNavigation
{
public partial class MasterPage : ContentPage
{
public ListView ListView { get { return listView; } }
public MasterPage ()
{
InitializeComponent ();
BackgroundColor = Color.FromHex ("ffffff");
var masterPageItems = new List<MasterPageItem> ();
masterPageItems.Add (new MasterPageItem {
Title = "DashBoard",
TargetType = typeof(DashBoard)
});
masterPageItems.Add (new MasterPageItem {
Title = "Schedules",
TargetType = typeof(SchedulesCS)
});
masterPageItems.Add (new MasterPageItem {
Title = "Master Data",
TargetType = typeof(MasterDataCS)
});
masterPageItems.Add (new MasterPageItem {
Title = "Audit Trail",
TargetType = typeof(AuditTrailCS)
});
listView.ItemsSource = masterPageItems;
}
}
}
For the 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="MasterDetailPageNavigation.MasterPage"
Padding="0,40,0,0"
Icon="hamburger.png"
Title="Medical">
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand">
<ListView x:Name="listView" VerticalOptions="FillAndExpand" SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell Text="{Binding Title}" ImageSource="{Binding IconSource}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
How do you do it?