I am having trouble with Use compiled bindings in a DataTemplate.
I added XamlComilation in App.xaml.cs
// https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xamlc
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Solution.Project
And I changed my DataTemplate to
<DataTemplate x:Key="RectLayerDataTemplate" x:DataType="{x:Type viewmodels:RectLayerViewModel}">
<forms:RectLayerView forms:ValueX="{Binding ValueX}"
forms:ValueY="{Binding ValueY}"
forms:ValueWidth="{Binding ValueWidth}"
forms:ValueHeight="{Binding ValueHeight}"
forms:Color="{Binding Color}" />
</DataTemplate>
However, DataTemplate does not applied to BindableLayout.ItemSource.
<AbsoluteLayout x:Name="CanvasLayout"
BindableLayout.ItemsSource="{Binding LayerViewModels}" />
Did you use AbsoluteLayout to make the layout overlap, so you think it didn't work?
I create a simple sample,with the DataTemplate and set it to the StackLayout BindableLayout,it works well.
the page.xaml:
<StackLayout x:Name="CanvasLayout" Orientation="Vertical"
BindableLayout.ItemsSource="{Binding LayerViewModels}" />
page.xam.cs:
public MyPage()
{
InitializeComponent();
BindingContext = new MyData();
LayerDataTemplate s = new LayerDataTemplate();
BindableLayout.SetItemTemplate(CanvasLayout, s.template);
}
MyData :
public class MyData
{
public ObservableCollection<RectLayerViewModel> LayerViewModels { set; get; } = new ObservableCollection<RectLayerViewModel>();
public MyData()
{
for (var x = 0; x < 6; x++)
{
LayerViewModels.Add(new RectLayerViewModel { Name = $"Added at {LayerViewModels.Count}", Type = 1 });
}
}
}
RectLayerViewModel:
public class RectLayerViewModel
{
public string Name { get; set; }
public int Type { get; set; }
}
DataTemplate:
<?xml version="1.0" encoding="UTF-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local ="clr-namespace:EntryCa"
x:Class="EntryCa.LayerDataTemplate">
<DataTemplate x:Key="RectLayerDataTemplate" x:DataType="local:RectLayerViewModel">
<Label Text="{Binding Name}"
TextColor="Red"
FontAttributes="Bold" />
</DataTemplate>
Sample usage of compiled bindings in a DataTemplate. Below ItemViewModel is the type of the items in a ListView.ItemsSource:
XAML:
xmlns:vm="clr-namespace:MyViewModels"
...
<DataTemplate x:Key="ListViewItemTemplate" x:DataType="vm:ItemViewModel" >
...
<ListView ... ItemTemplate="{StaticResource ListViewItemTemplate}"/>
ItemViewModel:
namespace MyViewModels
{
public class ItemViewModel : ViewModelPropertyBase
...
public abstract class ViewModelPropertyBase : INotifyPropertyChanged
...
Related
I am trying to bind the ItemSelected of a ListView to a View Model, but am experiencing some issues (due to my own misunderstands around how it all works).
I have view:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Local="clr-namespace:FireLearn.ViewModels"
x:Class="FireLearn.MainPage"
Title="Categories">
<ContentPage.BindingContext>
<Local:CategoryViewModel/>
</ContentPage.BindingContext>
<NavigationPage.TitleView>
<Label Text="Home"/>
</NavigationPage.TitleView>
<ListView
ItemsSource="{Binding Categories}"
HasUnevenRows="True"
IsPullToRefreshEnabled="True"
IsRefreshing="{Binding ListRefreshing, Mode=OneWay}"
RefreshCommand="{Binding RefreshCommand}"
ItemSelected="{Binding OnItemTappedChanged}"
SelectionMode="Single"
SelectedItem="{Binding SelectedCategory}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<HorizontalStackLayout
Padding="8"
VerticalOptions="Fill"
HorizontalOptions="Fill">
<Image Source="cafs_bubbles.png"
HeightRequest="64"
MaximumWidthRequest="64"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"/>
<VerticalStackLayout
Padding="8"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<Label Text="{Binding FormattedName}"
SemanticProperties.HeadingLevel="Level1"
FontSize="Title"
HorizontalOptions="Start"/>
<Label Text="{Binding ItemCount}"
FontSize="Subtitle"/>
<Label Text="{Binding Description}"
HorizontalOptions="Center"
LineBreakMode="WordWrap"
FontSize="Caption"
VerticalOptions="CenterAndExpand"
MaxLines="0"/>
</VerticalStackLayout>
</HorizontalStackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
This is linked to a view model:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FireLearn.Models;
namespace FireLearn.ViewModels
{
public partial class CategoryViewModel : ObservableObject
{
public ObservableCollection<CategoryModel> categories = new ObservableCollection<CategoryModel>();
public ObservableCollection<CategoryModel> Categories
{
get => categories;
set => SetProperty(ref categories, value);
}
public bool listRefreshing = false;
public bool ListRefreshing
{
get => listRefreshing;
set => SetProperty(ref listRefreshing, value);
}
public CategoryModel selectedCategory = new CategoryModel();
public CategoryModel SelectedCategory
{
get => selectedCategory;
set
{
SetProperty(ref selectedCategory, value);
// Tap(value);
}
}
public RelayCommand RefreshCommand { get; set; }
//public RelayCommand TapCellCommand { get; set; }
public CategoryViewModel()
{
loadFromSource();
RefreshCommand = new RelayCommand(async () =>
{
Debug.WriteLine($"STARTED::{ListRefreshing}");
if (!ListRefreshing)
{
ListRefreshing = true;
try
{
await loadFromSource();
}
finally
{
ListRefreshing = false;
Debug.WriteLine($"DONE::{ListRefreshing}");
}
}
});
}
public async Task loadFromSource()
{
HttpClient httpClient = new()
{
Timeout = new TimeSpan(0, 0, 10)
};
Uri uri = new Uri("https://somewebsite.co.uk/wp-json/wp/v2/categories");
HttpResponseMessage msg = await httpClient.GetAsync(uri);
if (msg.IsSuccessStatusCode)
{
var result = CategoryModel.FromJson(await msg.Content.ReadAsStringAsync());
Categories = new ObservableCollection<CategoryModel>(result);
}
Debug.WriteLine("List Refreshed");
}
public void OnItemTappedChanged(System.Object sender, Microsoft.Maui.Controls.SelectedItemChangedEventArgs e)
{
var x = new ShellNavigationState();
Shell.Current.GoToAsync(nameof(NewPage1),
new Dictionary<string, object>
{
{
nameof(NewPage1),
SelectedCategory
}
});
}
}
}
I get compiler error "No property, BindableProperty, or event found for "ItemSelected", or mismatching type between value and property" and am really unsure of how to resolve. If I let XAML create a new event for me, it adds it in MainPage.Xaml.Cs rather than the VM
ItemSelected expects an event handler which usually only exists in the View's code behind. Since the ViewModel shouldn't know anything about the View, it's better not to mix concepts. You have a couple of options to get around this without breaking the MVVM pattern.
Option 1: Use Event Handler and invoke method of ViewModel
First, set up the code behind with the ViewModel by passing it in via the constructor and also add the event handler, e.g.:
public partial class MainPage : ContentPage
{
private CategoryViewModel _viewModel;
public MainPage(CategoryViewModel viewModel)
{
_viewModel = viewModel;
}
public void OnItemSelectedChanged(object sender, SelectedItemChangedEventArgs e)
{
//call a method from the ViewModel, e.g.
_viewModel.DoSomething(e.SelectedItem);
}
//...
}
Then use the event handler from within the XAML:
<ListView
ItemsSource="{Binding Categories}"
HasUnevenRows="True"
IsPullToRefreshEnabled="True"
IsRefreshing="{Binding ListRefreshing, Mode=OneWay}"
RefreshCommand="{Binding RefreshCommand}"
ItemSelected="OnItemSelectedChanged"
SelectionMode="Single"
SelectedItem="{Binding SelectedCategory}">
<!-- skipping irrelevant stuff -->
</ListView>
Mind that this does not use bindings.
In your CategoryViewModel you could then define a method that takes in the selected item as an argument:
public partial class CategoryViewModel : ObservableObject
{
//...
public void DoSomething(object item)
{
//do something with the item, e.g. cast it to Category
}
}
Option 2: Use EventToCommandBehavior
Instead of handling the invocation of a ViewModel method from your code behind, you could also use the EventToCommandBehavior from the MAUI Community Toolkit:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Local="clr-namespace:FireLearn.ViewModels"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="FireLearn.MainPage"
Title="Categories">
<ContentPage.Resources>
<ResourceDictionary>
<toolkit:SelectedItemEventArgsConverter x:Key="SelectedItemEventArgsConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<ListView
ItemsSource="{Binding Categories}"
HasUnevenRows="True"
IsPullToRefreshEnabled="True"
IsRefreshing="{Binding ListRefreshing, Mode=OneWay}"
RefreshCommand="{Binding RefreshCommand}"
SelectionMode="Single"
SelectedItem="{Binding SelectedCategory}">
<ListView.Behaviors>
<toolkit:EventToCommandBehavior
EventName="ItemSelected"
Command="{Binding ItemSelectedCommand}"
EventArgsConverter="{StaticResource SelectedItemEventArgsConverter}" />
</ListView.Behaviors>
<!-- skipping irrelevant stuff -->
</ListView>
</ContentPage>
Then, in your ViewModel, you can define the ItemSelectedCommand:
public partial class CategoryViewModel : ObservableObject
{
[RelayCommand]
private void ItemSelected(object item)
{
//do something with the item, e.g. cast it to Category
}
// ...
}
This is the preferred way to do it. Option 1 is just another possiblity, but the EventToCommandBehavior is the better choice.
Note that this is an example using MVVM Source Generators (since you're already using the MVVM Community Toolkit). The full Command would normally be implemented like this:
public partial class CategoryViewModel : ObservableObject
{
private IRelayCommand<object> _itemSelectedCommand;
public IRelayCommand<object> ItemSelectedCommand => _itemSelectedCommand ?? (_itemSelectedCommand = new RelayCommand<object>(ItemSelected));
private void ItemSelected(object item)
{
//do something with the item, e.g. cast it to Category
}
// ...
}
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("..");
}
I am intending to bind the source for an image from a URL I am loading in my ViewModel
In the xaml file, it works just fine if I use:
<Image Source="https://example.com/image.jpg"/>
However, it won't work if I use
ViewModel
public string Image = {get; set;}
Image = "https://example.com/image.jpg";
XAML - I am setting the BindingContext to the ViewModel
<Image Source="{Binding Image}"/>
Any ideas?
Thanks
The code below should work. I set the binding in Xaml.
Xaml:
<ContentPage.BindingContext>
<local:ViewModel></local:ViewModel>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Image Source="{Binding Image}"></Image>
</StackLayout>
</ContentPage.Content>
ViewModel:
public class ViewModel
{
public string Image { get; set; }
public ViewModel()
{
Image = "https://aka.ms/campus.jpg"; //"https://example.com/image.jpg"
}
}
Or you could set the binding in code behind:
Xaml:
<ContentPage.Content>
<StackLayout>
<Image Source="{Binding Image}"></Image>
</StackLayout>
</ContentPage.Content>
Code behind:
public partial class Page2 : ContentPage
{
public Page2()
{
InitializeComponent();
this.BindingContext = new ViewModel();
}
}
public class ViewModel
{
public string Image { get; set; }
public ViewModel()
{
Image = "https://aka.ms/campus.jpg"; //"https://example.com/image.jpg"
}
}
I am learning to use Xamarin.Forms with prism, I want to populate a list view with a specific item from each object in my list. My list view shows up empty, but I don't see any errors.
I have tried using binding to bind to the Name variables in my objects, in my list. That showed up empty so I tried using Observable Collection to get my list and add each Name.
This is the Animal object model
using System;
namespace LearningPrism.Models
{
class Animal
{
public string Name { get; set; }
public int Age { get; set; }
public decimal Happiness { get; set; }
public void PrintBase()
{
Console.WriteLine($"Name: {Name}");
Console.WriteLine($"Age: {Age}");
Console.WriteLine($"Happy: {Happiness}");
Console.WriteLine();
}
}
}
I create my list using the Breed class as the object and have 3 functions bellow which get the filtered list for the type (unfiltered for get all breeds).
namespace LearningPrism.Models
{
class Breed
{
public static List<Animal> _breedList = new List<Animal>
{
new Animal
{
Id = Guid.NewGuid().ToString(),
Name = "Greyhound",
Type = BreedType.Dog
},
//There are 3 more Dog Animal breeds but I have removed them so it is easier to read
new Animal
{
Id = Guid.NewGuid().ToString(),
Name = "Bengal",
Type = BreedType.Cat
},
//There are 3 more Cat Animal breeds but I have removed them so it is easier to read
}
};
public static List<Animal> GetAllBreeds()
{
return _breedList;
}
public static List<Animal> GetBreedsByType(BreedType type)
{
switch (type)
{
case BreedType.Dog:
return (from Animal in _breedList where Animal.Type == BreedType.Dog select Animal).ToList();
case BreedType.Cat:
return (from Animal in _breedList where Animal.Type == BreedType.Cat select Animal).ToList();
default:
return _breedList;
}
}
}
}
Here is my view model:
namespace LearningPrism.ViewModels
{
public class PageAViewModel : ViewModelBase
{
private List<Breed> MyList { get; set; }
public PageAViewModel(INavigationService navigationService) : base(navigationService)
{
Title = "Hello Human";
}
private void LoadData()
{
ObservableCollection<Animal> myData = new ObservableCollection<Animal>(Breed.GetAllBreeds() as List<Animal>);
}
public override void OnNavigatingTo(INavigationParameters parameters)
{
base.OnNavigatingTo(parameters);
LoadData();
}
}
}
Here is the XAML code for the page
<?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="LearningPrism.Views.PageA" Title="{Binding Title}">
<Label Text="{Binding Title}" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" />
<StackLayout>
<ListView HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
ItemsSource="{Binding myData}">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="CenterAndExpand">
<Label Text="{Binding Breed.Name}" TextColor="Black"></Label>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
First, to bind your ListView to myData, myData must be a public property.
public ObservableCollection<Animal> myData { get; set; }
If each item in your list is an Animal, then your binding path would be Text="{Binding Name}" because Name is a property of Animal. There is no Breed property.
As Jason mentioned above
You property:
private List<Breed> MyList { get; set; }
Should be public. I would also recommend if your using prism to use a Property Change Event: Change your property to:
private List<Breed> _myList;
public List<Breed> MyList
{
get { return _myList; }
set { _myList = value; RaiseOnPropertyChanged(); }
}
Then Change your list XAML to:
<ListView HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
ItemsSource="{Binding MyList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout HorizontalOptions="CenterAndExpand">
<Label Text="{Binding Name}" TextColor="Black">
</Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I'm binding grouped listview to view model data source via xaml.
But when the app is launched the list is empty even when I populate it from code behind in ctor. When I declare the ListView x:Name="myList" and populate it from code behind it works, but it's not "binded" to the view model directly.
<?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:Atc.Obedy.ViewModels;assembly=Atc.Obedy"
x:Class="Atc.Obedy.MainPage" Title="Jídelníček">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness"
iOS="20, 40, 20, 20"
Android="20, 20, 20, 20"
WinPhone="20, 20, 20, 20" />
</ContentPage.Padding>
<ContentPage.BindingContext>
<viewModels:MainPageViewModel />
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
Orientation="Vertical"
Spacing="15">
<ListView BindingContext="{ Binding MealsGroups }" GroupDisplayBinding="{ Binding DisplayName }" IsGroupingEnabled="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding MealTitle}" />
<Button Image="icon.png" Command="{ Binding OrderCommand }" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Code behind:
public partial class MainPage : ContentPage
{
protected IMealsRepository MealsRepository { get; }
public MainPage()
{
MealsRepository = IoC.Container.Get<IMealsRepository>();
var mealGroups = new ObservableCollection<MealsGroup>();
foreach (var meals in MealsRepository.GetMeals(DateTime.MinValue, DateTime.MaxValue).Where(x => x.DayOfOrder != null).GroupBy(x=>x.DayOfOrder))
{
mealGroups.Add(ProvideMealsGroup(meals.Key.Value, meals));
}
InitializeComponent();
var viewModel = BindingContext as MainPageViewModel;
viewModel.MealsGroups = mealGroups;
}
This is View Model:
public class MainPageViewModel : INotifyPropertyChanged, IViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<MealsGroup> _mealGroups = null;
public ObservableCollection<MealsGroup> MealsGroups
{
get { return _mealGroups; }
set
{
_mealGroups = value;
OnPropertyChanged(nameof(MealsGroups));
}
}
public ICommand OrderCommand { get; set; }
public MainPageViewModel()
{
OrderCommand = new Command(() =>
{
Debug.WriteLine("MealsGroups");
});
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And meal group
public class MealsGroup : List<MealViewModel>
{
public string DisplayName { get; set; }
public string ShortName { get; set; }
public event MealGroupOrderSwitched MealGroupOrderSwitched;
public ICommand OrderCommand { get; set; }
public MealsGroup()
{
OrderCommand = new Command(() =>
{
Debug.WriteLine("MealGroup");
});
}
public void AddMeal(Meal meal)
{
var model = new MealViewModel
{
IsOrdered = meal.IsOrdered,
MealTitle = meal.MealTitle,
MealId = meal.MealId.Value,
DayOfOrder = meal.DayOfOrder.Value,
IsOrderable = meal.IsOrderable,
IsSoup = meal.IsSoup
};
model.MealOrdered += meaId =>
{
for (var i = 0; i < Count; i++)
{
var mealViewModel = this[i];
if (mealViewModel.MealId != meaId && mealViewModel.IsOrderable)
mealViewModel.IsOrdered = false;
}
MealGroupOrderSwitched?.Invoke(this);
};
Add(model);
}
}
but the android app when launched has empty list. Even when I added items in code behind in ctor.
Solved by changing in xaml ListView property BindingContext={binding MealsGroups} to ItemsSource={binding MealsGroups}