How to create a footer template in a Grouped ListView in Xamarin Form - forms

I'm working with Xamarin Form and C#. So, I have a listview in a Xamarin project which I enable the property IsGroupingEnabled. The items and header of items work perfectly but I cannot set a footer template.
I need to create a listview, items must be composed of:
Header: User information.
Items: List of subitems.
Footer: Actions buttons (shared, comments, etc).
This is part of my code:
Model:
public class Post
{
public long PostID { get; set; }
public string Name { get; set; }
public List<OptionDefault> OptionsDefault { get; set; }
}
public class OptionDefault
{
public long OptionTypeID { get; set; }
public string SubItemName { get; set; }
}
ViewModel
public class PostsViewModel
{
public InfiniteScrollCollection<Grouping<Post, OptionDefault>> Items { get; set; } = new InfiniteScrollCollection<Grouping<Post, OptionDefault>>();
public IPostsService repository;
public PostsViewModel(INavigation navigation)
{
repository = new PostsService();
Items = new InfiniteScrollCollection<Grouping<Post, OptionDefault>>
{
OnLoadMore = async () => await GetItems()
};
Items.LoadMoreAsync();
}
async Task<List<Grouping<Post, OptionDefault>>> GetItems()
{
IsWorking = true;
List<Grouping<Post, OptionDefault>> items = new List<Grouping<Post, OptionDefault>>();
try
{
// load the next page
var lists = await repository.GetList(Items.Count);
foreach (var item in lists)
{
for (int i = 0; i < item.OptionsDefault.Count; i++)
{
if ((i + 1) == item.OptionsDefault.Count)
item.OptionsDefault[i].LastItem = true;
}
var group = new Grouping<Post, OptionDefault>(item, item.OptionsDefault);
items.Add(group);
}
}
catch (Exception ex)
{
ErrorHelper.RegisterError(ex);
}
finally
{
IsWorking = false;
}
return items;
}
.xaml:
<ListView ItemsSource="{Binding Items}"
IsGroupingEnabled="True">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Margin="5,15,5,0">
<Label Text="{Binding Key.User.UserName}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Margin="5,0,5,5">
<Label Text="{Binding SubItemName}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I've tried using footertemplate, but it doesn't work:
<ListView.FooterTemplate>
<DataTemplate>
<StackLayout Margin="5,0,5,5">
<Label Text="This is my footer" />
</StackLayout>
</DataTemplate>
</ListView.FooterTemplate>

Whether it's <ListView.Footer> or <ListView.FooterTemplate>, the display position is the same, right at the bottom of the ListView.
Currently, ListView of xamarin form does not have this property to make each items have a footer.
Maybe you need to rethink the requirements and UI design of your app.

CollectionView is now available in Xamarin.Forms. It have both GroupHeaderTemplate and GroupFooterTemplate. You can use it to show the Group of data. The code to do so is identical with ListView

It is not <ListView.FooterTemplate> but <ListView.Footer>
<ListView.Footer>
<StackLayout Margin="5,0,5,5">
<Label Text="This is my footer" />
</StackLayout>
</ListView.Footer>
For more info, see:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/customizing-list-appearance#headers-and-footers

Related

Bound items display as name of item type, instead of contents of each item

//My Model
public class BookInfo
{
public string BookName { get; set; }
public string BookDescription { get; set; }
}
//my View Model
public ObservableCollection<BookInfo> Bookmodel { get; set; }
public BookRepoInfo()
{
Bookmodel = new ObservableCollection<BookInfo> { //**is this correct way.**
new BookInfo { BookName = "Object-Oriented Programming in C#", BookDescription = "Object-oriented programming is a programming paradigm based on the concept of objects" },
......
};
}
XAML page:
<ContentPage.BindingContext>
<local:BookRepoInfo />
</ContentPage.BindingContext>
<X:YList ItemsSource="{Binding Bookmodel}"></X:YList>
Load the list item using MVVM pattern
Assuming that YList is either a inheritance from ListView or CollectionView, you'll need to provide some sort of template which you want to apply to each cell of that list.
Right now what is happening is that it will just call the ToString() on the object that you put in.
Change your code to be something like:
<X:YList ItemsSource="{Binding Bookmodel}">
<X:YList.ItemTemplate>
<DataTemplate>
<VerticalStackLayout>
<Label Text="{Binding BookName}"/>
<Label Text="{Binding BookDescription}"/>
</VerticalStackLayout>
</DataTemplate>
</X:YList.ItemTemplate>
</X:YList>
More information is here: https://learn.microsoft.com/dotnet/maui/user-interface/controls/collectionview/populate-data?view=net-maui-7.0#define-item-appearance

UWP ObservableCollection notifies View on change

I want to create an UWP App with a Page, which shows a list of items, which are listed in an ObservableCollection. When I add or remove an item from the list, it should be also be added or removed from the view on the GUI. Or when I change a property of an item, it should also be changed in the view on the GUI. I tried the code from another page on this platform but it did not work.
What is necessary to notify the view (ListView or TreeView), which uses the ObservableCollection as ItemsSource, on changes (new item, removed item, value within item changed) within the ObservableCollection? NotifiyPropertyChanged(<name of ObservableCollection>) does not work in this case, but works on a simple string member.
Can anyone give me a small working example code from Model, View and ViewModel?
Kind regards,
Wolfgang
Can anyone give me a small working example code from Model, View and ViewModel?
Sure, Please check the following code. And ObservableCollection could notify UI Change without NotifiyPropertyChanged when add or delete item.
public sealed partial class MainPage : Page
{
private ObservableCollection<Book> Books { get; set; } = new ObservableCollection<Book>();
public MainPage()
{
this.InitializeComponent();
GetData();
}
private void GetData()
{
for (int i = 0; i < 25; i++)
{
var book = new Book { Name = $" Book{i}", Author = $"Author{i}" };
Books.Add(book);
}
MyListView.ItemsSource = Books;
}
private int count;
private void AddClick(object sender, RoutedEventArgs e)
{
count++;
Books.Insert(0, new Book { Name = $"NewBook{count}", Author = $"NewAuthor{count}" });
}
private void DelClick(object sender, RoutedEventArgs e)
{
if (Books.Count != 0)
{
Books.RemoveAt(0);
}
}
}
public class Book
{
public string Name { get; set; }
public string Author { get; set; }
}
Xaml
<Page.BottomAppBar>
<CommandBar>
<AppBarButton
Click="AddClick"
Icon="Add"
Label="Add"
/>
<AppBarButton
Click="DelClick"
Icon="Delete"
Label="Delete"
/>
</CommandBar>
</Page.BottomAppBar>
<Grid>
<ListView x:Name="MyListView">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Margin="23,0,0,0" Text="{Binding Author}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

Changing Property of ObservableCollection does nothing

In my Xamarin project I have a ListView, which gets populated by an ObservableCollection, which holds "Item" objects with some properties. If I add items to the collection the UI gets updated, but if I change only a property, it won't it does nothing. Even after an UI update through adding an item does nothing, although the property gets correctly changed. How can I make the UI refresh if a property gets changed?
BindableBase is a class from PRISM that implements INotifyPropertyChanged and DelegateCommand implements ICommand, btw.
Here's my XAML:
<ListView ItemsSource="{Binding ListItems}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell x:Name="viewCell">
<ContentView Padding="0,0,0,5"
HeightRequest="50">
<ContentView.GestureRecognizers>
<TapGestureRecognizer BindingContext="{Binding Source={x:Reference listView}, Path=BindingContext}"
Command="{Binding ItemTappedCommand}"
CommandParameter="{Binding Source={x:Reference viewCell}, Path=BindingContext}" />
</ContentView.GestureRecognizers>
<Frame OutlineColor="{Binding Color}" Padding="8">
<StackLayout Orientation="Horizontal" >
<Image x:Name="checkedImage"
HeightRequest="30"
WidthRequest="30"
BackgroundColor="{Binding Color}"
/>
<Label Text="{Binding Text}"
TextColor="{Binding Color}"
Margin="20,0,0,0"
VerticalTextAlignment="Center"
HorizontalOptions="FillAndExpand"/>
<Image Source="{Binding Image}" />
</StackLayout>
</Frame>
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here's my ViewModel:
public class DetailPageViewModel : BindableBase
{
public DetailPageViewModel()
{
_listItems.Add(new ViewModels.Item("#123456", "Test1", "XamarinForms.Assets.Yellow.png"));
_listItems.Add(new ViewModels.Item("#654321", "Test3", "XamarinForms.Assets.close.png"));
}
private ObservableCollection<Item> _listItems = new ObservableCollection<Item>();
public ObservableCollection<Item> ListItems
{
get { return _listItems; }
set { SetProperty(ref _listItems, value); }
}
public DelegateCommand<Item> ItemTappedCommand => new DelegateCommand<Item>(ItemTapped);
private void ItemTapped(Item listItem)
{
// Adding an item refreshes the UI.
_listItems.Add(new ViewModels.Item("#654321", "Test3", "XamarinForms.Assets.close.png"));
// But changing the color of an item does nothing. Not even after an UI refresh.
_listItems.ElementAt(_listItems.IndexOf(listItem)).Color="#987654";
}
}
And my Item class:
public class Item
{
public string Color { set; get; }
public ImageSource Check { set; get; }
public string Text { private set; get; }
public ImageSource Image { private set; get; }
public Item(string color, string text, string imageSource)
{
Check = ImageSource.FromResource("XamarinForms.Assets.checkmark-outlined-verification-sign.png");
Color = color;
Text = text;
Image = ImageSource.FromResource(imageSource);
}
}
This is because also your item class needs to implement INotifyPropertyChanged. In your case as you are using Prism you just need to make your item class extend BindableBase (Prism base class which already implements INotifyPropertyChanged for you).
Link: https://github.com/PrismLibrary/Prism/blob/a60d38013c02b60807e9287db9ba7f7506af0e84/Source/Prism/Mvvm/BindableBase.cs
That should make it work.
Also I see in you are doing this:
public ObservableCollection<Item> ListItems
{
get { return _listItems; }
set { SetProperty(ref _listItems, value); }
}
With ObservableCollections you don't need to raise the event manually as they already do it internally. They can be defined as regular properties.
public ObservableCollection<Item> ListItems {get; set;}

How to code a UWP MVVM ComboBox within an ItemTemplate?

I'm trying to figure out how to display a category for a selected item in a ComboBox which is itself within a GridView.ItemsTemplate. Nothing that I have tried so far has worked. I've tried using SelectedValue and SelectedValuePath.
I can display the Item's Category in a textbox from within the GridView.ItemsTemplate, but not the collection in a ComboBox.
// FiledNotesPageViewModel ViewModel
public class FiledNotesPageViewModel : ViewModelBase
{
public ObservableCollection<MemoryItem> NoteItems { get; set; } = new ObservableCollection<MemoryItem>();
}
// Public Properties
public int FiledNotesId
{
get
{
return _filedNotesId;
}
set
{
_filedNotesId = value;
OnPropertyChanged("FiledNotesId");
}
}
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public string Note
{
get
{
return _note;
}
set
{
_note = value;
OnPropertyChanged("Note");
}
}
public string Category
{
get
{
return _category;
}
set
{
_category = value;
OnPropertyChanged("Category");
}
}
// MemoryItem Model
public int FiledNotesId { get; set; }
public string Note { get; set; }
public string Name { get; set; }
public string Category { get; set; }
FiledNotesPage View
<GridView x:Name="FiledNotesGrid"
RelativePanel.Below="FilterNoteGrid"
ItemsSource="{x:Bind ViewModel.NoteItems}">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal"
Margin="5"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:MemoryItem">
<StackPanel Orientation="Vertical"
BorderBrush="{ThemeResource AppBarBorderThemeBrush}"
BorderThickness="2,2,2,4"
Margin="20"
Background="#FFF7F713" >
<TextBox x:Name="NoteNameTextBox"
Text="{x:Bind Name, Mode=TwoWay}"/>
<TextBox x:Name="NoteTextBox"
TextWrapping="Wrap"
AcceptsReturn="True"
Text="{x:Bind Note, Mode=TwoWay}"/>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5,0">Category:</TextBlock>
<ComboBox x:Name="NoteCategoryComboBox"
SelectedItem="{Binding FiledNotesId, Mode=TwoWay}"
MinWidth="125">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Category}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Your combobox doesn't have an ItemsSource
You need to do something like this...
In your CS File create an ObservableCollection property to bind to.
public ObservableCollection<int> FieldNoteIds {get; set;}
In your view, bind to your collection
<ComboBox ItemsSource="{Binding FieldNoteIds}">
Right now you're providing a template for how to display each item that is populated into the combobox but you're not giving it anything to populate.
MSDN Tutorial on ComboBoxes and Binding
https://code.msdn.microsoft.com/windowsdesktop/Best-ComboBox-Tutorial-5cc27f82#content

ViewCell not binding

I have the following listView with binding on my xaml page. I am binding data to it from my viewModel. But the values are not binding to the cell.
<ListView x:Name="historyList" ItemsSource="{Binding History}" HasUnevenRows="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal" Padding="10, 5, 10, 5" Spacing="0" HorizontalOptions="FillAndExpand" >
<Label Text="{Binding History.DayNumber}" HorizontalOptions="Center" FontSize="15" TextColor="Red" VerticalOptions="Center"></Label>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
My History object has the property DayNumber, but the value is not binding to the label?
Any suggestion? or am i missing something?
Thank you.
Edit:
Here is how I get my History List:
void GetAllHistory()
{
foreach(CheckInTimeHistory h in App.Database.GetHistoryCheckins(null))
{
CheckInHistoryItem hi = new CheckInHistoryItem();
hi.CheckedIn = (h.AutoCheckIn ? false : true);
hi.CheckInTime = h.CheckInTime.AddSeconds(-h.CheckInTime.Second).ToString("HH:mm-ss tt");
hi.DayNumber = h.CheckInTime.Day.ToString();
hi.DayText = h.CheckInTime.DayOfWeek.ToString().Substring(0, 2);
History.Add(hi);
}
}
public class CheckInHistoryItem
{
public String DayText, DayNumber, CheckInTime;
public Boolean CheckedIn;
}
You will see warnings like this in your debug output:
Binding: 'DayNumber' property not found on '....CheckInHistoryItem', target property: 'Xamarin.Forms.Label.Text'
The properties have to be C# properties. Just change your class to
public class CheckInHistoryItem
{
public String DayText { get; set; }
public String DayNumber { get; set; }
public String CheckInTime { get; set; }
public Boolean CheckedIn { get; set; }
}
and use DayNumber instead of History.DayNumber
<Label Text="{Binding DayNumber}" HorizontalOptions="Center" FontSize="15" TextColor="Red" VerticalOptions="Center"></Label>
Assuming History is a static var:
ItemsSource="{x:Static local:History}">
And change:
{Binding History.DayNumber}
to:
{Binding DayNumber}
Binding Basics:
https://developer.xamarin.com/guides/xamarin-forms/user-interface/xaml-basics/data_binding_basics/#Bindings_and_Collections