Sort ListView by pressing a button - xamarin

I have a ListView and wish to sort it up and down. Here is the xaml
<ListView x:Name="ListRegister" ItemsSource="{Binding Registrations}" HasUnevenRows="True" SelectionMode="Single">
<ListView.Behaviors>
<b:EventToCommandBehavior EventName="ItemTapped" Command="{Binding CommandGoDetail}" EventArgsParameterPath="Item" />
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Frame >
<StackLayout>
<Label Text="{Binding Date}" Style="{StaticResource registerItemDateText}"/>
</StackLayout>
</Frame>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
As well as the "button" (alternates between my SortUp() and sortDown() functions). The button changes a text to "up" and "down" so I know it works on screen.
<TapGestureRecognizer Command="{Binding CommandSort}"/>
These function seem to work
Registrations.Sort((x, y) => DateTime.Compare(y.Date, x.Date));
Registrations.Sort((x, y) => DateTime.Compare(x.Date, y.Date));
The list is correctly sorted Up or Down when one of those functions is called but when the button is Pressed, the list does not updates itself to the other sorting.
Where did I mess up ?
EDIT :
CommandSort code
private void HandleSort(Registration obj)
{
if(SortRecent)
{
SortRecent = false;
SortText = "Plus ancien";
SortImageSource = "lightDownArrow";
SortDown();
}
else
{
SortRecent = true;
SortText = "Plus récent";
SortImageSource = "lightUpArrow";
SortUp();
}
}
Registrations is of type :
List<Registration> Registrations
A Registration having a string name, and a DateTime date

You need to check what type of data is Registrations. It must be an Observable Collection in order to inform the UI about the changes made to the model. If not, You will need to manually update the list view to reflect the changes made to the model, in this case, the new sorting direction. You could use :
ListView.BeginRefresh(); & ListView.EndRefresh(); to force the update of the list view. But it is highly recommended to change your list to an observable collection.

Related

Focus on entry in listview on android

So i need to allow the user to get access to each entry field within the listview to enter a value, my code runs fine on iOS but once to try it on android i cant get access to the entry box, once the entry field is tapped it will show the cursor but it will not focus on the entry field and you cannot type anything as the keyboard doesnt appear. Does anyone have any idea how to fix this issue ?
<ListView x:Name="btnlist" Margin="0,0,0,20" Grid.Row="8" Grid.Column="1" Grid.ColumnSpan="2" AutomationId="ListMed"
CachingStrategy="RecycleElement" RowHeight="65" IsPullToRefreshEnabled="False" IsVisible="false" SeparatorColor="transparent"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell Appearing="Handle_Appearing1">
<local:XFDoneEntry x:Name="txtFrequency" Text="{Binding Count}" Day="{Binding Placeholder}" wdaynum="{Binding Wdaynum}" Placeholder="{Binding Placeholder}" Unfocused="Leave_box" VerticalOptions="Center" Keyboard="Numeric" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
//This is the code i use to find the entry boxes within the listview.
void Handle_Appearing1(object sender, System.EventArgs e)
{
var ViewcellAll1 = (ViewCell)sender;
ddoneentry = ViewcellAll1.View.FindByName<XFDoneEntry>("txtFrequency");
}
//this is the app with the listviews, depending on which days the user chooses the appropriate entry fields are populated within the second listview with entries.

XAML ListSource binding from multiple objects

I have a XAML view in which I am binding to a ViewModel and an ObservableCollection (Games) of type GAME_TBL
<ListView x:Name="GameListView"
ItemsSource="{Binding Games}"
ItemTapped="Handle_ItemTapped"
CachingStrategy="RecycleElement"
RowHeight="120">
I am referencing properties of that GAME_TBL object like so
<Label Text="{Binding GAME_NAME}"
Style="{StaticResource GameListTitle}" />
However, I want to style the list rows and tried to bind to an object that is not a property of GAME_TBL
<BoxView Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="5"
Grid.RowSpan="5"
BackgroundColor="{Binding BoxViewStyle}"/>
Code behind from same ViewModel
public Color BoxViewStyle
{
get { return _boxViewStyle; }
set
{
_boxViewStyle = value;
OnPropertyChanged(nameof(BoxViewStyle));
}
}
When the ViewModel is called I then set it like this
BoxViewStyle = Color.FromHex("#000000");
However it hasn't worked
I think it's something to do with me declaring the entire ListView to have an ItemSource which is the OS, but then trying to use an object outside of that without explicitly referencing it? Might be wrong about that.
The BindingContext for your list view is whatever data type Games is. Since the BoxViewStyle property lives in your ViewModel you can't bind to it from inside your ListView.ItemTemplate. You need to specify the source for your Binding.
Name your main ContentPage element. x:Name="mainElement"
When you set your BoxViewStyle binding specify the source:
<BoxView Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="5"
Grid.RowSpan="5"
BackgroundColor="{Binding BoxViewStyle, Source={x:Reference mainElement}"/>
no that was just to get things working stage by stage - I wanted to know I could bind the color first, then I was going to write a method that would alternate the colours every row
If you want to have the same color for ListView row, you can create BoxViewStyle color property in ViewModel, as ottermatic said that BoxViewStyle property is in ViewModel, so you can not bind it for ListView datetemplate, so you name your listview as list firstly, find list's BindingContext.BoxViewStyle.
<ListView x:Name="list" ItemsSource="{Binding games}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding GAME_NAME}" />
<BoxView BackgroundColor="{Binding BindingContext.BoxViewStyle, Source={x:Reference list}}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you want to alternate the colors every ListView row, I suggest you can create BoxViewStyle in model, according to ListView row index to change color.
<ListView x:Name="list" ItemsSource="{Binding games}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding GAME_NAME}" />
<BoxView BackgroundColor="{Binding BoxViewStyle}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Model.cs:
public class Game
{
public int Index { get; set; }
public string GAME_NAME { get; set; }
public Color BoxViewStyle
{
get
{
if (Index % 2 == 0)
{
return Color.Red;
}
else
{
return Color.Blue;
}
}
}
}
ViewModel.cs:
public class GameViewModel
{
public ObservableCollection<Game> games { get; set; }
public GameViewModel()
{
games = new ObservableCollection<Game>()
{
new Game(){Index=0,GAME_NAME="game 1"},
new Game(){Index=1,GAME_NAME="game 2"},
new Game(){Index=2,GAME_NAME="game 3"},
new Game(){Index=3,GAME_NAME="game 4"},
new Game(){Index=4,GAME_NAME="game 5"}
};
}
}
If my reply solved your issue, please remember to mark my reply as answer, thanks.

Adding a drop down area option on my list view

I’m wondering if you are able to add a drop down area option to a list view. For example when the user clicks on the specific row a drop down area appears below it showing various information and when they click it again it goes away.
Is this possible using xamarin forms ?
You cannot add a drop-down control inside a list view in Xamarin Forms. However, you could create a custom List View with custom Item Templates that contains a Stacklayout "hidden" from the user until the user taps on the row to re-enable the visibility of the "hidden" Stacklayout pushing down other rows. You may need to add an extra property to the ViewModel in order to control individual rows visibility.
If you check this C# corner guide it shows you how to come up with an Expandable ListView in Xamarin Forms.
You will have to make changes as per your requirement but your XAML would look something like this:
<ListView x:Name="HotelsList" BackgroundColor="White" IsGroupingEnabled="True" IsPullToRefreshEnabled="true" IsRefreshing="{Binding IsBusy, Mode=OneWay}" ItemsSource="{Binding Items}" RefreshCommand="{Binding LoadHotelsCommand}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" VerticalOptions="Center">
<Label VerticalOptions="Center" FontAttributes="Bold" FontSize="Medium" Text="{Binding .RoomName}" TextColor="Black" VerticalTextAlignment="Center" /> </StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label FontAttributes="Bold" FontSize="Small" Text="{Binding Name}" TextColor="Gray" VerticalTextAlignment="Center" />
<Image x:Name="ImgA" Source="{Binding StateIcon}" Margin="0,0,5,0" HeightRequest="20" WidthRequest="20" HorizontalOptions="End" />
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference currentPage}, Path=BindingContext.RefreshItemsCommand}" NumberOfTapsRequired="1" CommandParameter="{Binding .}" />
</Grid.GestureRecognizers>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
</ListView>
And your ViewModel as below:
public class HotelViewModel: ObservableRangeCollection < RoomViewModel > , INotifyPropertyChanged {
// It's a backup variable for storing CountryViewModel objects
private ObservableRangeCollection < RoomViewModel > hotelRooms = new ObservableRangeCollection < RoomViewModel > ();
public HotelViewModel(Hotel hotel, bool expanded = false) {
Hotel = hotel;
_expanded = expanded;
foreach(Room room in hotel.Rooms) {
Add(new RoomViewModel(room));
}
if (expanded) AddRange(hotelRooms);
}
public HotelViewModel() {}
private bool _expanded;
public bool Expanded {
get {
return _expanded;
}
set {
if (_expanded != value) {
_expanded = value;
OnPropertyChanged(new PropertyChangedEventArgs("Expanded"));
OnPropertyChanged(new PropertyChangedEventArgs("StateIcon"));
if (_expanded) {
AddRange(hotelRooms);
} else {
Clear();
}
}
}
}
public string StateIcon {
get {
if (Expanded) {
return "arrow_a.png";
} else {
return "arrow_b.png";
}
}
}
public string Name {
get {
return Hotel.Name;
}
}
public Hotel Hotel {
get;
set;
}
}
Check the above guide where the blogger and flawlessly explained all the aspects related to the use of this
Goodluck,
Revert in case of queries.

Can I use C# code to add a call to a command instead of a <Grid.GestureRecognizers>?

I have this code:
<ViewCell x:Name="co">
<Grid VerticalOptions="CenterAndExpand" Padding="20, 0">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OpenPickerCommand}" CommandParameter="{x:Reference coPicker}" NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
<Picker x:Name="coPicker" IsVisible="false" HorizontalOptions="End" SelectedIndexChanged="coPickerSelectedIndexChanged" ItemsSource="{Binding Order}"></Picker>
<Label x:Name="coLabel" HorizontalOptions="End"/>
</Grid>
</ViewCell>
Is there a way that I can in C# connect up a command to the tapping of the cell rather than have to use the XAML <Grid.GestureRecognizers> ?
Adding a GestureRecognizer to a ViewCell is a big no-no. A ViewCell exists within a ListView or TableView which have more than enough tapped options of their own. Adding a GestureRecognizer might confuse the OS as to which tap it should handle.
Your options for the GestureRecognizer are basically the following 3, but I advise against them in a scenario where you have a ListView/TableView.
Check out some of the ListView/ViewCell based alternatives I mention below in your situation.
1. GestureRecognizer - Add it in code
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.Tapped += (s, e) => {
// handle the tap
};
myGrid.GestureRecognizers.Add(tapGestureRecognizer);
2. GestureRecognizer - Use a command
When you use MVVM you can also use a command binding in C#:
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.SetBinding (TapGestureRecognizer.CommandProperty, "TapCommand");
myGrid.GestureRecognizers.Add(tapGestureRecognizer);
Which can then be bound in XAML:
<Grid>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}" />
</Grid.GestureRecognizers>
</Grid>
3. GestureRecognizer - Add it in XAML as you have done
<Grid>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OpenPickerCommand}" CommandParameter="{x:Reference coPicker}" NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
</Grid>
4. ViewCell - Tapped event
For the ViewCell you have a Tapped event:
<ViewCell Height="100" Tapped="OnTapped">
<ViewCell.View>
<StackLayout BackgroundColor="White" >
</StackLayout>
</ViewCell.View>
</ViewCell>
Which you can implement in code-behind:
void OnTapped (object sender, System.EventArgs e) { //your code}
5. ViewCell - Tapped command
When using MVVM you don't want to put a lot of business logic in the code-behind for your pages. In that case you can use a Behavior to convert the event to a command. A sample of that can be found here:
https://github.com/xamarin/xamarin-forms-samples/tree/master/Behaviors/EventToCommandBehavior/EventToCommandBehavior
6. ListView - ItemSelected
ListView itself also has an ItemSelected event. This can be handled the same way as the ViewCell Tapped event with both an event in code-behind or a Behavior to delegate it to a Command.
7. ListView - SelectedItem property
You can bind the SelectedItem to a property in your view model. On the setter you can perform your custom code.
<ListView
ItemsSource="{Binding YourItems}"
SelectedItem="{Binding YourSelectedItem, Mode=TwoWay}" >
</ListView>
And in code:
string _yourSelectedItem;
public string YourSelectedItem
{
get { return _yourSelectedItem; }
set {
_yourSelectedItem = value;
// Perform your custom functionality
}
}

FindViewById on Xamarin Forms?

I need some way to find View/object by its ID. I heared about FindViewById function, but it's not present in my ContentPage class. Where can I find it?
Context: I have ListView with buttons inside. I don't know how many buttons are there. When user clicks on one of those buttons, I get its ID and store globally. What I want to accomplish is to find this specific button, which id was stored in variable.
<StackLayout x:Name="chooseObjectButtons">
<ListView x:Name="SlotsList" ItemsSource="{Binding .}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout>
<Button Text="{Binding Text}" BackgroundColor="Gray" Clicked="SlotChosen" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Change the XAML to:
<ListView ItemsSource="{Binding Slots}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout>
<Button Text="{Binding Title}" BackgroundColor="Gray" Clicked="Handle_Clicked" Command="{Binding Select}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Handle click:
private Button LastButtonClicked;
void Handle_Clicked(object sender, System.EventArgs e)
{
if (LastButtonClicked != null)
{
// Change background here
}
LastButtonClicked = (Button)sender;
// Do stuff here.
}
To process the specific command for each button use:
public List<SlotsButtons> Slots
{
get
{
return new List<SlotsButtons>
{
new SlotsButtons
{
Title = "T1",
Select = new Command(()=>{
// do stuff here when clicked.
})
},
new SlotsButtons
{
Title = "T2",
Select = new Command(()=>{
// do stuff here when clicked.
})
}
};
}
}
NOTE: Initial question answer.
In Xamarin Forms the class ContentPage is a Partial class.
One part is automatically generated from XAML and the other represents the code behind.
The XAML generated Partial class has the code to find the views by name.
The correct name is FindByName and you should't need to use this in your partial class because it its already made in the generated partial class.
If you want to access a view in your code behind just give it a name in XAML.
There is an XAML example:
<Button x:Name="button" ></Button>
And in your code behind you could do something like:
button.BorderWidth = 3;
If you still need to find a view for some reason, do this:
var button = this.FindByName<Button>("button");

Resources