<ListView x:Name="myList" ItemTapped="OnMyItemTapped" ItemsSource="{Binding myList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout> CONTENT HERE </StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
where myList is an ObservableCollection
Desired: Displays all list items immediately at once without animation
Actual: Displays list items one at a time (similar to the add item animation)
Any ideas?
(This sequential displaying of items is significantly more noticeable when the list of items within the list view is larger)
The only way I've found to do it is to use a custom renderer.
These are the lines I'm using to disable the insert, delete and reload rows animations:
if (e.OldElement != null)
{
InsertRowsAnimation = UITableViewRowAnimation.None;
DeleteRowsAnimation = UITableViewRowAnimation.None;
ReloadRowsAnimation = UITableViewRowAnimation.None;
}
This needs to be placed in your CustomListViewRenderer that is inheriting from Xamarin forms ListViewRenderer.
I put mine in the OnElementChanged event.
You can use a platform specific property to disable animations:
<ContentPage ...
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout Margin="20">
<ListView ios:ListView.RowAnimationsEnabled="false">
</ListView>
</StackLayout>
</ContentPage>
Read more here:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/ios/listview-row-animations
Related
Im trying to be methodical in my aproach to this school project but I am having XAML difficulties. So far I have been able to get my main page to look exactly how I want it to except for not being able to print any data using a list.view with a basic test string.
Heres the entire page cs file: https://gist.github.com/GermanHoyos/2ffed121e9d9b7026e0b1c6f36c2ce9a
Heres an image of the code in question(specificaly line 85):
Souldnt line 85 appear inside of the List.view as "test".
The reason im trying to get the string "test" to appear in the bottom most box is because eventually I would like to populate an entire list of data in this bottom most section. But I cant begin to even try things like {binding var} if I cant get a basic string to apear first.
Any help would be appreciated!
Heres list code:
<ListView x:Name = "fullViewOfTermsCourses"
BackgroundColor="PowderBlue"
HeightRequest="1"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell IsEnabled="True">
<StackLayout Orientation="Horizontal" HorizontalOptions="CenterAndExpand">
<Label Text="test" VerticalOptions="CenterAndExpand" TextColor="Blue"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have FlexLayout that I use to display the contents of the collection. I want to realize the possibility that when you click on an element of the collection, the command is executed. I want to make sure that when you click on an item, another view is loaded that loads data that corresponds to the selected day. How do I get collection items to react to clicks?
<ContentPage.Resources>
<DataTemplate x:Key="ColorItemTemplate">
<Grid Margin="5"
HeightRequest="120"
WidthRequest="105">
<BoxView Color="Azure" />
<Label Text="{Binding Day}"
HorizontalOptions="Center"
VerticalOptions="Center" />
</Grid>
</DataTemplate>
</ContentPage.Resources>
<StackLayout Margin="10">
<Label Text="CollectionView"
FontSize="30"
FontAttributes="Bold"
HorizontalOptions="Center" />
<Label Text="{Binding MyCollection.Count, StringFormat='Count: {0}'}" />
<ScrollView>
<FlexLayout Direction="Row"
Wrap="Wrap"
AlignItems="Center"
AlignContent="Center"
BindableLayout.ItemsSource="{Binding MyCollection}"
BindableLayout.ItemTemplate="{StaticResource ColorItemTemplate}"/>
</ScrollView>
</StackLayout>
but when clicking on an item, where do you want to send data to?
In the screenshot above, you can see 20 objects in the collection. They are created in the class constructor and they display the time at which they were created.
PowerTrainings = new ObservableCollection<PowerTraining>();
for (int i = 0; i < 20; i++)
{
PowerTrainings.Add(new PowerTraining() { DayOfTraining = dateTime });
}
According to my idea, every time a user (in particular, I, because I do it for myself) will choose a particular day from the history, another view will be loaded, which will display the exercises that he did on that day. But how to correctly implement in the XAML code a code that will determine that this particular element of the collection was clicked (in which dates are displayed) and pass this date to another view, which, based on the property DayOfTraining , will make a query to the database with classes on this day and form another collection?
If I represent it by analogy with WPF, then I would create a
MouseDoubleClick event and pass the object to another view through the
SelectedIndex property. But in the case of Xamarin, I don't understand
(yet) how everything works.
I could deploy your app to my emulator, but when clicking on an item, where do you want to send data to?
Depending on the scenario, there are several ways to pass data between two Views.
A simple method is to use Xamarin.Forms MessagingCenter.
The Xamarin.Forms MessagingCenter class implements the publish-subscribe pattern, allowing message-based communication between components that are inconvenient to link by object and type references. This mechanism allows publishers and subscribers to communicate without having a reference to each other, helping to reduce dependencies between them.
Publish a message
Publishers notify subscribers of a message with one of the MessagingCenter.Send overloads. The following code example publishes a Hi message:
MessagingCenter.Send<MainPage>(this, "Hi");
Subscribe to a message
Subscribers can register to receive a message using one of the MessagingCenter.Subscribe overloads.
MessagingCenter.Subscribe<MainPage> (this, "Hi", (sender) =>
{
// Do something whenever the "Hi" message is received
});
Unsubscribe from a message
Subscribers can unsubscribe from messages they no longer want to receive. This is achieved with one of the MessagingCenter.Unsubscribe overloads:
MessagingCenter.Unsubscribe<MainPage>(this, "Hi");
Update:
And I don't understand how you can find out which element the user
clicked on
You can add TapGestureRecognizer for your DataTemplate.
I modified your DataTemplate and add TapGestureRecognizer for it. You can refer the following code:
<ContentPage.Resources>
<DataTemplate x:Key="ColorItemTemplate">
<Frame Margin="5"
HeightRequest="120"
BackgroundColor="Azure"
WidthRequest="105">
<Label Text="{Binding DayOfTraining}"
HorizontalOptions="Center"
VerticalOptions="Center" />
<Frame.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.TypeListSelectedCommand, Source={x:Reference flexLayout}}" CommandParameter="{Binding}"></TapGestureRecognizer>
</Frame.GestureRecognizers>
</Frame>
</DataTemplate>
</ContentPage.Resources>
In class TrainingViewModel,add command TypeListSelectedCommand as follows:
public ICommand TypeListSelectedCommand => new Command<PowerTraining>(selectedItem);
void selectedItem(PowerTraining obj)
{
PowerTraining item = obj as PowerTraining;
System.Diagnostics.Debug.WriteLine("clicked item = " + item.DayOfTraining);
}
Note:
1.flexLayout is the x:Name of your FlexLayout:
<FlexLayout x:Name="flexLayout" />
In addition, you can add TapGestureRecognizer like this:
<ContentPage.Resources>
<DataTemplate x:Key="ColorItemTemplate">
<Frame Margin="5"
HeightRequest="120"
BackgroundColor="Azure"
WidthRequest="105">
<Label Text="{Binding DayOfTraining}"
HorizontalOptions="Center"
VerticalOptions="Center" />
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"></TapGestureRecognizer>
</Frame.GestureRecognizers>
</Frame>
</DataTemplate>
</ContentPage.Resources>
And in CalendarOfTraining.xaml.cs,add function TapGestureRecognizer_Tapped:
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
Frame boxView = sender as Frame;
PowerTraining SelectedItem = (PowerTraining)boxView.BindingContext;
System.Diagnostics.Debug.WriteLine(" clicked item = " + SelectedItem.DayOfTraining);
}
I have a CollectionView that shows a list of Categories object like so:
And I'd like to have a list of SubCategories to be listed right after it as if it was the same list.
I know CollectionView only takes one item but I wanted to know if there was a way to have two lists that can behave as one when you scroll
Thank you for your help
you can use 2 StackLayout right after each other and set BindableLayout
like this :
<StackLayout Orientation="Horizontal">
<StackLayout Orientation="Horizontal" BindableLayout.ItemsSource="{Binding CategoryItemSource}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<!--CategoryItemTemplate-->
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
<StackLayout Orientation="Horizontal" BindableLayout.ItemsSource="{Binding SubCategoryItemSource}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<!--SubCategoryItemTemplate-->
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
in this case first stacklayout show you category items and second stacklayout show subcategory just right after that
two possible solutions :
1- Use inheritance to make both Category and SubCategory share a commune Parent then build an index where you make sure that each element is listed after his parent.
2- use a nested collection view to display subcategories for each category.
I've got a list of objects that I'm binding to a ListView and using a DataTemplate to show in a Xamarin app. So far, pretty simple. But the kicker is that I want one of the controls (a label specifically) to update continually.
So far this is what I have...
<ListView x:Name="ItemsListView"
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
RefreshCommand="{Binding LoadItemsCommand}"
IsPullToRefreshEnabled="true"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"
CachingStrategy="RecycleElement"
ItemSelected="OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10">
<Label Text="{Binding Title}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" />
<mycountdown:TimerLabel Text="{Binding TimeRemainingString}"
Style="{DynamicResource ListItemDetailTextStyle}"
FontSize="13" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And the code behind for my TimerLabel class...
public class TimerLabel : Label
{
private bool beating;
public TimerLabel()
{
StartHeartbeat();
}
public void StartHeartbeat()
{
//only start beating again if not currently beating.
if (!beating)
{
beating = true;
Heartbeat();
}
}
public void StopHeartbeat()
{
beating = false;
}
async void Heartbeat()
{
while (beating)
{
this.Text = DateTime.UtcNow.ToLongTimeString();
}
}
}
This works, but the problem is that the heartbeat for each item in the list keeps running even when I navigate away from the page.
Ideally, the heartbeat would only run for items that are visible on screen, but I would settle for letting them all run and just disabling them when I leave the page.
The problem is I can't figure out how to access the StopHeartbeat() method from the page's code behind or the view model.
It's not going to be limited to only Labels either. I will end up having a few other controls that need to update in the UI thread continuously, but this is the simplest one to start with.
If there's another way I should be doing this, please say so.
Thanks!
if you are Bind List in ListItem Source then please change it from List to ObservableCollection it will work let me know if any question or can you upload viewModel code as well
Answered by the top comment in my post.
I have following list view (the item source is set outside and a list of strings):
<?xml version="1.0" encoding="utf-8" ?>
<ListView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XXX.EditItemsList">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding .}"/>
<Button Text="Delete"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
With clicking the button, I want to delete the current item (string) in the list. How is this possible?
Thanks for your help :)
In principle i think the answer given by #Krzysztof Skowronek is right, I will simply try to elaborate on it, and avoid the use of ViewModel since it seems you are not using it (although the use of it is a Best Practice on Xamarin Forms).
Following your own code, i wrote the following code in 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:DeleteButton"
x:Class="DeleteButton.MainPage">
<ListView x:Name="listView"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding .}"/>
<Button Text="Delete" Clicked="Delete"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
About this part of the solution i would give the following comments:
Note the use of ConntentPage instead of ListView at the top of the XAML, is that intentional?
Then, note the addition of x:Name on ListView. It will be used to communicate with the ListView from the code behind.
Additionally notice the use of HasUnevenRows set to True. This causes the ListView to adjust automatically the height of the rows.
Finally see that in Button i have set the event Clicked to "Delete", which is the name of the event handler in the code behind as you will see.
On the code behind i wrote:
using System;
using System.Collections.ObjectModel;
using Xamarin.Forms;
namespace DeleteButton
{
public partial class MainPage : ContentPage
{
ObservableCollection<String> list;
public MainPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
list = new ObservableCollection<string>()
{
"Task 1", "Task 2", "Task 3", "Task 4", "Task 5",
"Task 6", "Task 7", "Task 8", "Task 9", "Task 10"
};
listView.ItemsSource = list;
}
public void Delete(Object Sender, EventArgs args)
{
Button button = (Button)Sender;
StackLayout listViewItem = (StackLayout)button.Parent;
Label label = (Label)listViewItem.Children[0];
String text = label.Text;
list.Remove(text);
}
}
}
There i define the list of strings as an ObservableCollection (ObservableCollection causes the ListView to get a notification each time it changes, so that the ListView updates its contents, see the docu for more details).
Then i set the ItemSource property of the ListView to the collection of Strings, as you have already done.
Finally comes the EventHandler Delete, called by the Click event on Button, in XAML. The algorithm here is quite simple:
First the sender is cast to a Button (we know that the object firing the event is a Button).
Then we walk up the hierarchy tree up to the StackLayout containing the Button and the Label and retrieve the first child of it, which we know is the Label.
Once we have the Label we retrieve its Text property and call the Remove method of the collection to get ride of that item.
And that's it.
Note: If i would implement this functionality myself i would rather define a collection of objects which would containt a Text property as well as an Id property in order to remove exactly the element tapped. In the code above, if the collection contains two identical strings, the EventHandler would go simply for the first occurrence.
I hope this helps you to find the right way to solve your problem.
If you don't want to use Commands, you can use the Button's Clicked event. IE,
<Button Text="Delete" Clicked="HandleDeleteButtonClicked" />
Then in your code behind file,
private void HandleDeleteButtonClicked(object sender, EventArgs e)
{
// Assuming your list ItemsSource is a list of strings
// (If its a list of some other type of object, just change the type in the (cast)):
var stringInThisCell = (string)((Button)sender).BindingContext;
// Now you can delete stringInThisCell from your list.
myList.Remove(stringInThisCell);
}
If we are willing to keep MVVM approach, then in your View, name the ContentPage (or whichever root element is there) and use it as Source to bind command:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="canaraydash.View.InviteListPage"
x:Name="InvitesView">
<ListView x:Class="XXX.EditItemsList">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding .}" />
<Button Text="Delete"
Command="{Binding Path=BindingContext.AcceptRequestCommand, Source={x:Reference InvitesView}}"
CommandParameter="{Binding .}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
and in your ViewModel, define 'AcceptRequestCommand' command!
Create a DeleteItem command with a parameter in your control (or it's ViewModel preferably) and then in xaml:
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding .}"/>
<Button Text="Delete" Command="{Binding Source={this should be your VM for the whole thing}, Path=DeleteItem}" CommandParameter="{Binding}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
In command:
list.Remove(parameter);
If the the list is obeservable, it will disappear.
Here's my solution on the problem
<DataTemplate>
<Button Text="{Binding DisplayName}" Image="tab_about.png" ContentLayout="Top,0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
Command="{Binding Source={RelativeSource AncestorType={x:Type vm:AnimationViewModel}}, Path=AnimationCommand}"
CommandParameter="{Binding .}" />
</DataTemplate>
And the model view file is a normal command :
public ObservableCollection<LedAnimation> AnimationList { get; }
public Command<LedAnimation> AnimationCommand { get; }
public AnimationViewModel()
{
Title = "Animation";
AnimationList = new ObservableCollection<LedAnimation>();
AnimationCommand = new Command<LedAnimation>(OnLedAnimationTap);
_serverService.AnimationCapabilities.ForEach(x => AnimationList.Add(x));
}
private void OnLedAnimationTap(LedAnimation animation)
{
if (animation == null)
return;
Console.WriteLine($"VM:{animation.Name}");
}