Xamarin Forms CollectionView Grouping Not Working With Intellisense - xamarin

The Issue
I am getting extremely confused with how grouping works in the CollectionView.
I am able to get an output as expected using the recommended example from the Xamarin Documentation with the code below:
CollectionView XAML
<CollectionView IsGrouped="True" ItemsSource="{Binding CategoriesList}" RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.5}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="10" />
</CollectionView.ItemsLayout>
<CollectionView.GroupHeaderTemplate>
<DataTemplate>
<Label Text="{Binding Name}" />
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
<CollectionView.ItemTemplate>
<DataTemplate>
<SwipeView>
<SwipeView.LeftItems>
<SwipeItems>
<SwipeItem Text="Delete" IconImageSource="CategoryPhoto" />
</SwipeItems>
</SwipeView.LeftItems>
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem Text="Edit" IconImageSource="CategoryPhoto" />
</SwipeItems>
</SwipeView.RightItems>
<SwipeView.Content>
<StackLayout BackgroundColor="Red">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.EditProduct, Source={x:Reference Name=MainView}}" CommandParameter="{Binding ID}" />
</StackLayout.GestureRecognizers>
<StackLayout Orientation="Horizontal" Padding="15">
<Image Source="CategoryPhoto" />
<StackLayout Orientation="Vertical">
<Label Text="{Binding Name}" TextColor="Black" />
<Label Text="doy" TextColor="Black" />
</StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand">
<Label Text="⊖" TextColor="Black" VerticalTextAlignment="Center" RelativeLayout.HeightConstraint="{ConstraintExpression Property=Height, Factor=1, Type=RelativeToParent}">
<Label.GestureRecognizers>
<!--<TapGestureRecognizer Command="{Binding Path=BindingContext.RemoveItem, Source={x:Reference Name=MainView}}" CommandParameter="{Binding ID}" />-->
</Label.GestureRecognizers>
</Label>
<Label x:Name="QuantityLabel" Text="{Binding Quantity}" TextColor="Black" VerticalTextAlignment="Center" />
<Label Text="⊕" TextColor="Black" VerticalTextAlignment="Center">
<Label.GestureRecognizers>
<!--<TapGestureRecognizer Command="{Binding Path=BindingContext.PurchaseItem, Source={x:Reference Name=MainView}}" CommandParameter="{Binding ID}" />-->
</Label.GestureRecognizers>
</Label>
</StackLayout>
</StackLayout>
<Rectangle Fill="LightGray" HeightRequest="2" />
</StackLayout>
</SwipeView.Content>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Product Class
using System;
using System.ComponentModel;
using SQLite;
using SQLiteNetExtensions.Attributes;
namespace TakeItAwayPOS.Structures
{
public class Product
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public int Stock { get; set; }
[ForeignKey(typeof(Category))]
public int Category { get; set; }
//for adding to orders
[Ignore]
private int quantity { get; set; }
public int Quantity
{
get => quantity;
set { quantity = value; QuantityChanged(); }
}
public delegate void PropertyChangedEvent(object sender, PropertyChangedEventArgs args);
public event PropertyChangedEvent PropertyChanged;
private void QuantityChanged()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
}
}
}
CategoryGroup Class
using System;
using System.Collections.Generic;
namespace TakeItAwayPOS.Structures
{
public class CategoryGroup : List<Product>
{
public string Name { get; private set; }
public CategoryGroup(string name, List<Product> products) : base(products)
{
Name = name;
}
}
}
EditProduct Command
public ICommand EditProduct
{
get
{
return new Command<string>((param) =>
{
Console.WriteLine("Triggered!");
LoadProduct(Convert.ToInt32(param));
});
}
}
My issue is with the GestureRecogniser for the StackLayout inside of the DataTemplate:
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.EditProduct, Source={x:Reference Name=MainView}}" CommandParameter="{Binding ID}" />
</StackLayout.GestureRecognizers>
the CommandParameter binding wont work and the Command is only fired if I replace "{Binding ID}" with "0":
When I hover over all of the binding items in the CollectionView, intellisense references them to the CategoryGroup type instead of the Product type that would be expected, the odd thing is that the other binding values except the Command all work even though intellisense references them to the wrong type.
My two questions are:
How would I go about trying to fix that command
Can I fix the intellisense so that it recognises when grouping is true (or is this a known issue?)
Thanks in advance!
Solved
Thanks #Jason!
For some reason earlier in my code, the CommandParameter didn't want to work as an int and so I had used this long winded method that converted to a string and then back to an int within the Command's lambda method. Turns out I didn't need this and the fix was just to change the parameter type of the Command to int as so:
public ICommand EditProduct
{
get
{
return new Command<int>((param) =>
{
Console.WriteLine("Triggered!");
LoadProduct(param);
});
}
}

ID is an int, so you need to use Command<int>, not Command<string>

Related

CollectionView items animation

I'm creating application with chat function. I made simple 'typing indicator' that shows new item in collectionView (Label) with text 'User is typing...'. So now i would like to animate this Label or add some animation like 3 dots that indicates user is typing. Is it somehow possible to make something like this? Any help appreciated, Thank you.
#EDIT
Code below describes my Chat layout:
<ContentPage.Content>
<StackLayout x:Name="content_stackLayout">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<CollectionView
x:Name="chat_collectionView"
Grid.Row="0"
VerticalOptions="Start"
HorizontalOptions="Center"
ItemSizingStrategy="MeasureAllItems"
SelectionMode="None"
ItemsSource="{Binding Messages}"
ItemsUpdatingScrollMode="KeepLastItemInView"
>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal" HorizontalOptions="{Binding messageAlignment}" Margin="30,5,30,5">
<Image
Source="{Binding image}"
Margin="{Binding imageMargin}"
HeightRequest="40"
WidthRequest="40"
Aspect="AspectFit" />
<Frame
BorderColor="Black"
CornerRadius="{Binding cornerRadius}"
BackgroundColor="{Binding kolorWiadomosci}"
Margin="{Binding messageMargin}">
<Label
Text="{Binding message}"
FontAttributes="{Binding textAttribute}"
x:Name="label"
FontSize="Title"
VerticalOptions="CenterAndExpand" />
</Frame>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<StackLayout
Grid.Row="1"
Orientation="Horizontal"
x:Name="sendMessage_stackLayout"
HeightRequest="80"
Padding="8"
>
<Entry
Margin="10,10,0,5"
Placeholder="your message..."
ClassId="message_entry"
x:Name="message_entry_label"
HorizontalOptions="FillAndExpand"
ClearButtonVisibility="WhileEditing"
Keyboard="Chat"
ReturnType="Send"
TextColor="Black"
BackgroundColor="#2596be"
/>
<Button
WidthRequest="200"
HeightRequest="30"
Text="Send"
FontSize="Title"
x:Name="button_send_label"
HorizontalOptions="End"
Clicked="Button_Clicked"
BorderWidth="5"
BorderColor="Black"
CornerRadius="5"
/>
</StackLayout>
</Grid>
</StackLayout>
</ContentPage.Content>
I've got binding with viewModel which receives messages from another App with SignalR, creates a new messages and adds them to List called "Messages" like below:
public class Message
{
public string message{ get; set; }
//public Thickness messageMargin{ get; set; }
public string image{ get; set; }
//public Thickness imageMargin{ get; set; }
public FontAttributes textAttribute{ get; set; }
public string author{ get; set; }
public LayoutOptions messageAlignment{ get; set; }
public Color textColor{ get; set; }
public double cornerRadius { get; set; }
}
So now when another person in second App is typing, it fires SignalR boolean method that my 'Chat App' receives. If UserTyping = true - App adds new message saying 'User is typing'. I would like to animate it somehow or if not possible, develop another solution for this behaviour.

PropertyChanged Event doesn't reflect new Image.Source on first run

I'm working on mobile app that kinda simulates how to build a Desktop Computer by dragging and dropping an image in order. I work with DropGestureRecognizer with AllowDrop set to True for the Drop Zone Image controls and DragGestureRecognizer with CanDrag set to True for the Drag Image Objects.
The idea is the Drag Image Objects will get dragged and dropped to a Drop Zone Image Control and if they dropped the correct Drag Image, the Drop Zone will accept this as the Image.Source and another Drop Zone will be stacked in front of it to work with the next Desktop Computer component. Otherwise, it would get rejected and the Image.Source will be set to empty or null.
However, i'm encountering a weird issue that, on the first incorrect image dragged, the Image.Source gets reset to empty or null by property but on the actual UI, it doesn't. On the succeeding EventHandler execution, it does get reset. I have a TapGestureRecognizer on the Drop Zone to check if there is an Image.Source set or not.
I'm sorry if my explanation is a bit confusing because English is not my native language and i'm having a hard time explaining it. So please refer to below example in GIF and my code:
View (.xaml):
<ContentPage.Content>
<Grid RowDefinitions="Auto,60,*"
ColumnDefinitions="*">
<RelativeLayout Grid.Row="0" Grid.Column="0" HorizontalOptions="Center">
<Image x:Name="imgCaseDropZone"
RelativeLayout.XConstraint="0"
RelativeLayout.YConstraint="0"
HeightRequest="{Binding ScreenWidth}"
WidthRequest="{Binding ScreenWidth}"
PropertyChanged="CaseDropZone_PropertyChanged"
BackgroundColor="LightGray">
<Image.GestureRecognizers>
<DropGestureRecognizer AllowDrop="True" />
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
</Image.GestureRecognizers>
</Image>
</RelativeLayout>
<Label Grid.Row="1" Grid.Column="0"
x:Name="lblDirections"
Text="Directions: Drag the components below in its proper order to the drop zone above."
TextColor="Black"
Padding="10"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<ScrollView Grid.Row="2"
Orientation="Horizontal"
Margin="5">
<StackLayout Orientation="Horizontal">
<!--#region Case -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding CaseIsVisible}">
<Image Grid.Row="0"
x:Name="imgCaseDragObject"
Source="{Binding CaseImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding CaseLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Case Cover -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding CaseCoverIsVisible}">
<Image Grid.Row="0"
Source="{Binding CaseCoverImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding CaseCoverLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Case Screw -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding CaseScrewIsVisible}">
<Image Grid.Row="0"
Source="{Binding CaseScrewImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding CaseScrewLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Hard Disk Drive -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding HardDiskDriveIsVisible}">
<Image Grid.Row="0"
Source="{Binding HardDiskDriveImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding HardDiskDriveLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Heatsink -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding HeatsinkIsVisible}">
<Image Grid.Row="0"
Source="{Binding HeatsinkImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding HeatsinkLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Memory Module -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding MemoryModuleIsVisible}">
<Image Grid.Row="0"
Source="{Binding MemoryModuleImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding MemoryModuleLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Motherboard -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding MotherboardIsVisible}">
<Image Grid.Row="0"
Source="{Binding MotherboardImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding MotherboardLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Motherboard Screw -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding MotherboardScrewIsVisible}">
<Image Grid.Row="0"
Source="{Binding MotherboardScrewImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding MotherboardScrewLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Power Supply -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding PowerSupplyIsVisible}">
<Image Grid.Row="0"
Source="{Binding PowerSupplyImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding PowerSupplyLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
<!--#region Processor -->
<Grid RowDefinitions="*,Auto"
IsVisible="{Binding ProcessorIsVisible}">
<Image Grid.Row="0"
Source="{Binding ProcessorImgSource}">
<Image.GestureRecognizers>
<DragGestureRecognizer CanDrag="True" />
</Image.GestureRecognizers>
</Image>
<Label Grid.Row="1"
Text="{Binding ProcessorLabel}"
TextColor="White"
FontSize="Medium"
BackgroundColor="Gray"
HorizontalTextAlignment="Center" />
</Grid>
<!--#endregion-->
</StackLayout>
</ScrollView>
</Grid>
</ContentPage.Content>
View Code (.xaml.cs):
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AssemblyPage : ContentPage
{
string caseSource;
public AssemblyPage()
{
InitializeComponent();
}
int dragCount = 0;
private void CaseDropZone_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Source")
{
caseSource = imgCaseDropZone.Source.ToString().Split(':').Last().Trim();
if (string.IsNullOrEmpty(caseSource))
{
return;
}
if (caseSource != "img_assembly_case.png")
{
imgCaseDropZone.Source = string.Empty;
lblDirections.Text = $"Drag Count: {++dragCount}";
}
}
}
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
Application.Current.MainPage.DisplayAlert("", $"{imgCaseDropZone.Source}", "OK");
}
}
ViewModel (.cs):
public class AssemblyViewModel : BaseVM
{
public double ScreenWidth => Xamarin.Forms.Application.Current.MainPage.Width;
#region Case
bool caseIsVisible;
public bool CaseIsVisible
{
get => caseIsVisible;
set => SetProperty(ref caseIsVisible, value);
}
public string CaseImgSource { get; }
public string CaseLabel { get; }
#endregion
#region CaseCover
bool caseCoverIsVisible;
public bool CaseCoverIsVisible
{
get => caseCoverIsVisible;
set => SetProperty(ref caseCoverIsVisible, value);
}
public string CaseCoverImgSource { get; }
public string CaseCoverLabel { get; }
#endregion
#region HardDiskDrive
bool hardDiskDriveIsVisible;
public bool HardDiskDriveIsVisible
{
get => hardDiskDriveIsVisible;
set => SetProperty(ref hardDiskDriveIsVisible, value);
}
public string HardDiskDriveImgSource { get; }
public string HardDiskDriveLabel { get; }
#endregion
#region CaseScrew
bool caseScrewIsVisible;
public bool CaseScrewIsVisible
{
get => caseScrewIsVisible;
set => SetProperty(ref caseScrewIsVisible, value);
}
public string CaseScrewImgSource { get; }
public string CaseScrewLabel { get; }
#endregion
#region Heatsink
bool heatsinkIsVisible;
public bool HeatsinkIsVisible
{
get => heatsinkIsVisible;
set => SetProperty(ref heatsinkIsVisible, value);
}
public string HeatsinkImgSource { get; }
public string HeatsinkLabel { get; }
#endregion
#region MemoryModule
bool memoryModuleIsVisible;
public bool MemoryModuleIsVisible
{
get => memoryModuleIsVisible;
set => SetProperty(ref memoryModuleIsVisible, value);
}
public string MemoryModuleImgSource { get; }
public string MemoryModuleLabel { get; }
#endregion
#region Motherboard
bool motherboardIsVisible;
public bool MotherboardIsVisible
{
get => motherboardIsVisible;
set => SetProperty(ref motherboardIsVisible, value);
}
public string MotherboardImgSource { get; }
public string MotherboardLabel { get; }
#endregion
#region MotherboardScrew
bool motherboardScrewIsVisible;
public bool MotherboardScrewIsVisible
{
get => motherboardScrewIsVisible;
set => SetProperty(ref motherboardScrewIsVisible, value);
}
public string MotherboardScrewImgSource { get; }
public string MotherboardScrewLabel { get; }
#endregion
#region PowerSupply
bool powerSupplyIsVisible;
public bool PowerSupplyIsVisible
{
get => powerSupplyIsVisible;
set => SetProperty(ref powerSupplyIsVisible, value);
}
public string PowerSupplyImgSource { get; }
public string PowerSupplyLabel { get; }
#endregion
#region Processor
bool processorIsVisible;
public bool ProcessorIsVisible
{
get => processorIsVisible;
set => SetProperty(ref processorIsVisible, value);
}
public string ProcessorImgSource { get; }
public string ProcessorLabel { get; }
#endregion
public AssemblyViewModel()
{
CaseIsVisible = true;
CaseImgSource = "img_assembly_case.png";
CaseLabel = "Case";
CaseCoverIsVisible = true;
CaseCoverImgSource = "img_assembly_case_cover.png";
CaseCoverLabel = "Case Cover";
CaseScrewIsVisible = true;
CaseScrewImgSource = "img_assembly_case_screw.png";
CaseScrewLabel = "Case Screw";
HardDiskDriveIsVisible = true;
HardDiskDriveImgSource = "img_assembly_hard_disk_drive.png";
HardDiskDriveLabel = "Hard Disk Drive";
HeatsinkIsVisible = true;
HeatsinkImgSource = "img_assembly_heat_sink.png";
HeatsinkLabel = "Heatsink";
MemoryModuleIsVisible = true;
MemoryModuleImgSource = "img_assembly_memory_module.png";
MemoryModuleLabel = "Memory Module";
MotherboardIsVisible = true;
MotherboardImgSource = "img_assembly_motherboard.png";
MotherboardLabel = "Motherboard";
MotherboardScrewIsVisible = true;
MotherboardScrewImgSource = "img_assembly_motherboard_screw.png";
MotherboardScrewLabel = "Motherboard Screw";
PowerSupplyIsVisible = true;
PowerSupplyImgSource = "img_assembly_power_supply.png";
PowerSupplyLabel = "Power Supply";
ProcessorIsVisible = true;
ProcessorImgSource = "img_assembly_processor.png";
ProcessorLabel = "Processor";
}
}
Current Build demo in GIF: https://imgur.com/a/oLeM9DV (i can't directly link the GIF because its too big)
Because when you trigger the PropertyChanged event, the drag and drop action has already been completed and the image has been automatically set to the target.
We could make that judgment as soon as we drag it over the target with DragOver event.Then make an action when the drag and drop is complete with Drop event.
For example,change the codes of the Image (imgCaseDropZone):
<Image x:Name="imgCaseDropZone"
RelativeLayout.XConstraint="0"
RelativeLayout.YConstraint="0"
RelativeLayout.WidthConstraint="400"
RelativeLayout.HeightConstraint="400"
BackgroundColor="LightGray"
>
<Image.GestureRecognizers>
<DropGestureRecognizer AllowDrop="{Binding CaseIsVisible}" DragOver="CaseDropZone_DragOver" Drop="CaseDropZone_Drop"/>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
</Image.GestureRecognizers>
</Image>
in the code behind:
private void CaseDropZone_DragOver(object sender, DragEventArgs e)
{
FileImageSource file = (FileImageSource)e.Data.Image;
string name = file.File;
if (name != "img_assembly_case.png")
{
e.Data.Image = string.Empty; // set the value string.Empty when drag it over the target,then it will fill the empty source to the image.
}
}
private async void CaseDropZone_Drop(object sender, DropEventArgs e)
{
var ctx = (BindingContext as AssemblyViewModel);
FileImageSource source = (FileImageSource)await e.Data.GetImageAsync();
string name = source.File;
if (name == "img_assembly_case.png")
{
ctx.CaseIsVisible = false;
imgMotherboardDropZone.IsVisible = true;
}
else
{
Global.Score.PCAssembly -= 5;
DisplayAlert($"Score: {Global.Score.PCAssembly}", "The pre-requisite component for this part has not yet been placed.", "OK");
}
}
Other Images are modified like this.

How to implement a command to delete an item from my database in xamarinforms mvvm style

Currently i have a clicked function that delete an items :
async void DeleteButtonClicked(object sender, EventArgs e)
{
ImageButton button = sender as ImageButton;
var agenda = button.BindingContext as Agenda;
await App.Database.DeleteAgendaAsync(agenda);
await Navigation.PopAsync();
}
I want to implement this MVVM style with a command, the basic of my app is that i have an AgendaPage that loads items in a collection view, right now i want to be able to call the command on this icon and so it delete the item.
Thanks for your help.
Currently in my PageViewModel i only have this
I have to pass the current agenda to the DeleteAgendaAsync() method but it's unclear to me where to get it.
public Command DeleteAgendaCommand { get; set; }
public AgendaPageViewModel()
{
DeleteAgendaCommand = new Command(async () => await DeleteAgenda());
}
async Task DeleteAgenda()
{
await App.Database.DeleteAgendaAsync();
}
AgendaDatabase.cs in the Database folder
using System;
using System.Collections.Generic;
using System.Text;
using SQLite;
using Calculette.Models;
using System.Threading.Tasks;
using Calculette.ViewModel;
namespace Calculette.Database
{
public class AgendaDatabase
{
readonly SQLiteAsyncConnection database;
public AgendaDatabase(string dbPath)
{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<Agenda>().Wait();
}
// Get all agenda
public Task<List<Agenda>> GetAgendasAsync()
{
return database.Table<Agenda>().ToListAsync();
}
// Get specific agenda
public Task<Agenda> GetAgendaAsync(int id)
{
return database.Table<Agenda>()
.Where(i => i.ID == id)
.FirstOrDefaultAsync();
}
// Insert new agenda (save)
public Task<int> SaveAgendaAsync(Agenda agenda)
{
if (agenda.ID != 0)
{
return database.UpdateAsync(agenda);
}
else
{
return database.InsertAsync(agenda);
}
}
//Delete specific agenda
public Task<int> DeleteAgendaAsync(Agenda agenda)
{
return database.DeleteAsync(agenda);
}
public Task<int> AddAgendaAsync(Agenda agenda)
{
return database.InsertAsync(agenda);
}
}
}
Agenda.cs in the Models folder
[Table("Agenda")]
public class Agenda
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Topic { get; set; }
public string Duration { get; set; }
public DateTime Date { get; set; }
This is the NewFormViewModel.cs which is used to create new agenda items to the collectionview, it feels like i would need to access all the items added there, but im unsure on how to do that for the DeleteCommand
using Calculette.Database;
using Calculette.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
namespace Calculette.ViewModel
{
class NewFormViewModel : BaseViewModel
{
public Command AgendaSaveFormCommand { get; set; }
public NewFormViewModel()
{
// Initialise la valeur du DatePicker a celle d'aujourd'hui
this.Date = DateTime.Now;
// Commande pour la sauvegarde sur la page NewFormPage ( voir SaveForm() plus bas)
AgendaSaveFormCommand = new Command(async () => await SaveForm(), () => !IsBusy);
}
// Création des propriétés d'un agenda
private string topic;
public string Topic
{
get => topic;
set
{
topic = value;
NotifyPropertyChanged();
}
}
private string duration;
public string Duration
{
get => duration;
set
{
duration = value;
NotifyPropertyChanged();
}
}
private DateTime date;
public DateTime Date
{
get => date;
set
{
date = value;
NotifyPropertyChanged();
}
}
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set
{
isBusy = value;
NotifyPropertyChanged();
AgendaSaveFormCommand.ChangeCanExecute();
}
}
public int ID { get; }
// Methode qui enregistre un agenda et l'ajoute a la collection d'agenda de AgendaPage
async Task SaveForm()
{
IsBusy = true;
await Task.Delay(4000);
IsBusy = false;
// Agenda agenda = new Agenda();
//ObservableCollection<Agenda> agenda = new ObservableCollection<Agenda>();
Agenda agenda = new Agenda();
agenda.Topic = Topic;
agenda.Date = Date;
agenda.Duration = Duration;
await App.Database.SaveAgendaAsync(agenda);
await Application.Current.MainPage.DisplayAlert("Save", "La tâche a été enregistrée", "OK");
await Application.Current.MainPage.Navigation.PopAsync();
}
}
AgendaPage.xaml
<CollectionView Grid.Row="2" Margin="25" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
SelectionMode="Single" x:Name="AgendaCollection" ItemsSource="{Binding Agendas}"> <!--ItemsSource="{Binding AngedaCollection}" -->
<CollectionView.Header>
<StackLayout Orientation="Horizontal" Spacing="220">
<Label Text="Agenda" TextColor="Black" FontSize="18"/>
<StackLayout Orientation="Horizontal">
<ImageButton Source="iconplus.png" HeightRequest="30" WidthRequest="30" Clicked="GoToNewFormPage"></ImageButton>
<ImageButton Source="iconmoins.png" HeightRequest="30" WidthRequest="30" Clicked="DeleteButtonClicked"></ImageButton>
</StackLayout>
</StackLayout>
</CollectionView.Header>
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="20"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate >
<DataTemplate>
<pv:PancakeView HasShadow="True" BackgroundColor="White" VerticalOptions="StartAndExpand "
HorizontalOptions="FillAndExpand">
<Grid VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<BoxView BackgroundColor="{Binding Color}" WidthRequest="3" HorizontalOptions="Start"
VerticalOptions="FillAndExpand"/>
<Expander Grid.Column="1" >
<Expander.Header>
<Grid HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3.5*"/>
</Grid.ColumnDefinitions>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="{Binding Date, StringFormat='{0:dd}'}" TextColor="#008A00" FontSize="27"
HorizontalOptions="Center"/>
<Label Text="{Binding Date, StringFormat='{0:MMMM}'}" TextColor="Black" FontSize="10"
HorizontalOptions="Center" Margin="0,-10,0,0" FontAttributes="Bold"/>
<ImageButton Source="iconplus.png" HorizontalOptions="Center" HeightRequest="30" WidthRequest="30" Clicked="GoToFormPage"></ImageButton>
</StackLayout>
<BoxView Grid.Column="1" BackgroundColor="#F2F4F8" WidthRequest="1" HorizontalOptions="Start"
VerticalOptions="FillAndExpand"/>
<StackLayout x:Name="topicLayout" Grid.Column="2" HorizontalOptions="Start" VerticalOptions="Center" Margin="20">
<Label Text="{Binding Topic}" TextColor="#008A00" FontSize="15" FontAttributes="Bold"/>
<Label Text="{Binding Duration}" TextColor="#2F3246" FontSize="12" Margin="0,-10,0,0"/>
<ImageButton Source="iconmoins.png" HeightRequest="30" WidthRequest="30" Command="{Binding Source={x:Reference AgendaCollection}, Path=AgendaPageViewModel.DeleteAgendaCommand}"
CommandParameter="{Binding .}"></ImageButton>
</StackLayout>
</Grid>
</Expander.Header>
<Grid HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3.5*"/>
</Grid.ColumnDefinitions>
<BoxView Grid.Column="1" BackgroundColor="#F2F4F8" WidthRequest="1" HorizontalOptions="Start"
VerticalOptions="FillAndExpand"/>
<StackLayout Grid.Column="2" Spacing="10">
<Label Text="Tâches" TextColor="Black" FontSize="15" Margin="20,0"/>
<StackLayout BindableLayout.ItemsSource="{Binding Speakers}" HorizontalOptions="Start" VerticalOptions="Center" Margin="20,0,0,20">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label TextColor="#2F3246" FontSize="12">
<Label.FormattedText>
<FormattedString>
<FormattedString.Spans>
<Span Text="{Binding Time}"/>
<Span Text=" - "/>
<Span Text="{Binding Name}" FontAttributes="Bold"/>
</FormattedString.Spans>
</FormattedString>
</Label.FormattedText>
</Label>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</Grid>
</Expander>
</Grid>
</pv:PancakeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Im not sure where you have your delete button, so here are two possible ways of doing this:
1) You select an Agenda from the CollectionView and then use one Button to delete this agenda:
Add the SelectedItem as a Property to the ViewModel like that:
ObservableCollection<Agenda> agendas = new ObservableCollection<Agenda>();
public ObservableCollection<Agenda> Agendas { get => agendas; set => agendas = value; }
public Agenda SelectedItem { get; set; }
public Command Delete { get; set; }
public ViewModel()
{
Agendas.Add(new Agenda() { Name = "test" });
Agendas.Add(new Agenda() { Name = "test2" });
Agendas.Add(new Agenda() { Name = "test3" });
Delete = new Command(new Action<object>((obj) =>
{
DeleteAsync(SelectedItem);
}));
}
private void DeleteAsync(Agenda selectedItem)
{
//delete Agenda here
}
To use this simply bind the SelectedItem property of the CollectionView to your ViewModel:
<StackLayout>
<StackLayout.BindingContext>
<local:ViewModel/>
</StackLayout.BindingContext>
<CollectionView ItemsSource="{Binding Agendas}"
SelectionMode="Single"
SelectedItem="{Binding SelectedItem}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame>
<StackLayout>
<Label Text="{Binding Name}" FontSize="120" Margin="12"/>
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="Delete" Command="{Binding Delete}" />
</StackLayout>
2) You have a button to delete an Agenda for each Agenda:
You can setup your ViewModel similar to this:
ObservableCollection<Agenda> agendas = new ObservableCollection<Agenda>();
public ObservableCollection<Agenda> Agendas { get => agendas; set => agendas = value; }
public Command Delete { get; set; }
public ViewModel()
{
Delete = new Command(new Action<object>((obj) =>
{
DeleteAsync((Agenda) obj);
}));
}
private void DeleteAsync(Agenda selectedItem)
{
//delete Agenda here
}
Then you can set this up in xaml like this:
<StackLayout>
<StackLayout.BindingContext>
<local:ViewModel/>
</StackLayout.BindingContext>
<CollectionView x:Name="AgendaCollection"
ItemsSource="{Binding Agendas}" >
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame>
<StackLayout>
<Label Text="{Binding Topic}" FontSize="120" Margin="12"/>
<Button Text="Delete"
Command="{Binding Source={x:Reference AgendaCollection}, Path=BindingContext.Delete}"
CommandParameter="{Binding .}"/>
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
Note you can directly Bind to a Command, you dont have to use ButtonClicked and than pass it on.
In this case you have to change the Binding Source to the CollectionView (give that a x:Name and Bind to that) because the Binding Context inside the DataTemplate will be set to the Children of the ItemSource (the individual Agendas). That way the command is automatically executed without the Clicked event.
To pass on the Agenda on which delete was clicked to the Command you can set the CommandParameter. Here it Binds to itself (As i said CollectionView will set the DataContext of its elements to the corresponding individual element.)

How to display questions and answers in Listview (in Xamarin.Forms)?

I'm trying to connect something with something. I added a button in one list from which I would like it to redirect to another page. In this second page I would like to display a list of questions and answers related to the previous page where I have "Routes" In the api from questions and answers I have RouteId which I associated with pressing the route.id button (tmp.RouteId == route.Id)
My api responsible for questions and answers from RouteId:
{"Id":1,"Question1":"W którym roku?","AnswersString":"1987,1980,1957","Answers":["1987","1980","1957"],"GoodAnswer":2,"Correct":null,"RouteId":212,"Route":null}
I only pull out of it (Answers, Question 1, Good Answer)
The code responsible for pressing the button on the list page:
private async void OnQuizClicked(object sender, EventArgs e)
{
var button = sender as ImageButton;
var route = button?.BindingContext as Routes;
var token = Application.Current.Properties["MyToken"].ToString();
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.GetStringAsync("URL from questions and answers");
List<Question> punkty = JsonConvert.DeserializeObject<List<Question>>(response);
List<Question> list = new List<Question>();
foreach (Question tmp in punkty)
{
if (tmp.RouteId == route.Id)
{
list.Add(tmp);
}
}
await Navigation.PushAsync(new UserQuestionPage());
}
Class Question:
public class Question
{
public int Id { get; set; }
public IList<string> Answers { get; set; }
public int GoodAnswer { get; set; }
public string Question1 { get; set; }
public int RouteId { get; set; }
}
I would like to see questions and answers in the UserQuestionPage class as a list. Sorry for my english.
Listview from routes
<ListView Margin="0,0,0,0" x:Name="routesList"
HorizontalOptions="Center"
VerticalOptions="Center"
ItemTapped="OnTapped"
SelectionMode="None"
ItemsSource="{Binding Routes}"
SeparatorColor="#90B320"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<Label Text="Nr." TextColor="Black"
FontAttributes="Bold" FontSize="15"
HorizontalTextAlignment="Center"
Margin="5,10,20,10"/>
<Label Text="{Binding Id}" TextColor="Black"
FontAttributes="Bold" FontSize="15"
HorizontalTextAlignment="Center"
Margin="-25,10,0,0"/>
<Label Text="{Binding Name}"
TextColor="Black"
HorizontalTextAlignment="Center"
FontAttributes="Bold"
FontSize="15"
Margin="20,10,20,10"/>
<Label Text="{Binding Description}"
TextColor="Black"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
FontAttributes="Italic"
FontSize="10"
Margin="10"/>
<ImageButton x:Name="quizButton" Source="quiz2.png"
HorizontalOptions="EndAndExpand" VerticalOptions="CenterAndExpand"
Clicked="OnQuizClicked" BackgroundColor="#FDDA8A" CornerRadius="20"
WidthRequest="70" HeightRequest="70" Margin="3"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Listview from UserQuestionPage:
<ListView ItemsSource="{Binding Answers}" ItemTapped="ListView_ItemTapped" Margin="10,0">
</ListView>
public partial class UserQuestionPage : ContentPage
{
public List<Question> pointsList = new List<Question>();
Question pytania = new Question();
public UserQuestionPage(RouteQuestions siema)
{
InitializeComponent();
pointsList = siema.routeQuestions;
foreach (Question tmp in siema.routeQuestions)
{
elo.ItemsSource = pointsList;
}
}
}
public class RouteQuestions
{
public RouteQuestions(List<Question> list)
{
routeQuestions = list;
}
public List<Question> routeQuestions { get; set; }
}
I am sending a picture with a problem
<ListView x:Name="elo" ItemsSource="{Binding pointList}" Margin="10,0">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Grid.Column="0" Text="{Binding Question1}" />
<Label Grid.Column="1" Text="{Binding Answers[0]}"/>
<Label Grid.Column="2" Text="{Binding Answers[1]}"/>
<Label Grid.Column="3" Text="{Binding Answers[2]}"/>
<Label Grid.Column="4" Text="{Binding Answers[3]}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Next picture

Serialize Class Xamarin.Forms

I’m trying to serialize class but I can’t do it.
I have a class below:
public class DadosTitulo
{
public string nome { get; set; }
public string numeroCpfCnpj { get; set; }
public string logradouro { get; set; }
public string numero { get; set; }
public string complemento { get; set; }
public string bairro { get; set; }
public string cep { get; set; }
public string municipio { get; set; }
public string uf { get; set; }
public double valor { get; set; }
public string numeroDocumento { get; set; }
public string dataVencimento { get; set; }
public string celularDestino { get; set; }
public bool registroProducao { get; set; }
}
And in my MainPage.xaml.cs I have:
private async void btnAdicionar_Clicked(object sender, EventArgs e)
{
var retorno = "";
var titulo = new DadosTitulo
{
nome = txtNome.Text,
numeroCpfCnpj = txtNumeroCpfCnpj.Text,
logradouro = txtLogradouro.Text,
numero = txtNumero.Text,
complemento = txtComplemento.Text,
bairro = txtBairro.Text,
cep = txtCep.Text,
municipio = txtMunicipio.Text,
uf = txtUF.Text,
valor = Convert.ToDouble(txtValor.Text),
numeroDocumento = txtNumeroDocumento.Text,
dataVencimento = DataVencimento.Date.ToString("dd/MM/yyyy"),
celularDestino = txtDddCelularDestino.Text + txtNumCelularDestino.Text,
registroProducao = RegistraProducao
};
var registroService = new RegistroBoletoService();
retorno = registroService.RegistrarBoleto(titulo);
await DisplayAlert("Json", retorno, "OK");
}
Property Values from 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:AppRegistraBoletoXF"
x:Class="AppRegistraBoletoXF.MainPage">
<StackLayout Orientation="Vertical">
<StackLayout>
<Label Text="Registra Boleto" TextColor="Indigo" FontSize="Medium" />
</StackLayout>
<StackLayout>
<Label Text="Informe os dados do Título:" TextColor="Black" FontSize="Small" />
</StackLayout>
<StackLayout>
<Entry x:Name="txtNome" Placeholder="Nome" HorizontalOptions="Start"
VerticalOptions="StartAndExpand" HeightRequest="35" WidthRequest="300" FontSize="Small"/>
<Entry x:Name="txtNumeroCpfCnpj" Placeholder="CPF/CNPJ" HorizontalOptions="Start"
VerticalOptions="StartAndExpand" HeightRequest="35" WidthRequest="300" FontSize="Small" />
<StackLayout Orientation="Horizontal">
<Entry x:Name="txtLogradouro" Placeholder="Logradouro" HorizontalOptions="StartAndExpand" VerticalOptions="Start"
HeightRequest="35" WidthRequest="160" FontSize="Small" />
<Entry x:Name="txtNumero" Placeholder="Número" HorizontalOptions="StartAndExpand" VerticalOptions="Start"
HeightRequest="35" WidthRequest="50" FontSize="Small" />
<Entry x:Name="txtComplemento" Placeholder="Complemento" HorizontalOptions="StartAndExpand" VerticalOptions="Start"
HeightRequest="35" WidthRequest="90" FontSize="Small" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Entry x:Name="txtBairro" Placeholder="Bairro" HorizontalOptions="StartAndExpand" VerticalOptions="Start"
HeightRequest="35" WidthRequest="180" FontSize="Small" />
<Entry x:Name="txtCep" Placeholder="CEP" HorizontalOptions="StartAndExpand" VerticalOptions="Start"
HeightRequest="35" WidthRequest="120" FontSize="Small" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Entry x:Name="txtMunicipio" Placeholder="Município" HorizontalOptions="StartAndExpand" VerticalOptions="Start"
HeightRequest="35" WidthRequest="250" FontSize="Small" />
<Entry x:Name="txtUF" Placeholder="UF" HorizontalOptions="StartAndExpand" VerticalOptions="Start"
HeightRequest="35" WidthRequest="50" FontSize="Small" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Entry x:Name="txtDddCelularDestino" Placeholder="DDD" HorizontalOptions="StartAndExpand" VerticalOptions="Start"
HeightRequest="35" WidthRequest="50" FontSize="Small" />
<Entry x:Name="txtNumCelularDestino" Placeholder="Celular Destino" HorizontalOptions="StartAndExpand" VerticalOptions="Start"
HeightRequest="35" WidthRequest="250" FontSize="Small" />
</StackLayout>
<Entry x:Name="txtNumeroDocumento" Placeholder="Número do Título" HorizontalOptions="Start"
VerticalOptions="StartAndExpand" HeightRequest="35" WidthRequest="300" FontSize="Small"/>
<Entry x:Name="txtValor" Placeholder="Valor do Título" HorizontalOptions="Start"
VerticalOptions="StartAndExpand" HeightRequest="35" WidthRequest="300" FontSize="Small"/>
<StackLayout Orientation="Horizontal">
<Label Text="Registro em Produção: " TextColor="Black" FontSize="Small" HorizontalOptions="Start"
VerticalOptions="StartAndExpand" />
<DatePicker DateSelected="DataSelecionada" HorizontalOptions="Start" VerticalOptions="StartAndExpand">
<DatePicker.Format>dd/MM/yyyy</DatePicker.Format>
</DatePicker>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Registro em Produção: " TextColor="Black" FontSize="Small" HorizontalOptions="Center"
VerticalOptions="StartAndExpand" />
<Switch IsToggled="False" Toggled="RegistrarProducao" HorizontalOptions="Center"
VerticalOptions="StartAndExpand" />
</StackLayout>
<Button HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand" HeightRequest="40" Text="Registrar Título"
Clicked="btnAdicionar_Clicked" FontSize="Small"/>
</StackLayout>
</StackLayout>
</ContentPage>
In RegistroBoletoService:
public string RegistrarBoleto(DadosTitulo dadosTitulo)
{
var data = JsonConvert.SerializeObject(dadosTitulo);
return data;
}
data allways return “{}”:
I have made a test calling RegistroBoletoService from Console Project, in the same Solution, and the result was as expected: data contains json
The interesting point that I noticed: when I have instantiated the class in MainPage.xaml.cs the behavior was diferent from Console Project, with Non-Public Members in the class:
Console Instantiate:
I think that this is the problem, anyone have an idea what is it happening ? Why when I have instantiated the class in the MainPage I get different behavior from the Console ?
Thanks a lot and sorry my English.
PS.: Project in .NET Standard; I’m not using MVVM in this case; I tried testing with the linker SDK Assemblies Only and None.

Resources