I'm trying to implement a ListView in Xamarin Forms. A list that we can check or choose the item that we want. I want a single item selection at a time.
My xaml file :
ListView x:Name="listview" ItemSelected="OnItemSelected" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout HorizontalOptions="StartAndExpand" Orientation="Horizontal">
<StackLayout Padding="20,0,0,0" VerticalOptions="Center" Orientation="Vertical">
<Label Text="{Binding .}" YAlign="Center" FontSize="Medium" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
My xaml.cs file :
public void OnItemSelected (object sender, SelectedItemChangedEventArgs e) {
if (e.SelectedItem == null) return;
// add the checkmark in the event because the item was clicked
// be able to check the item here
DisplayAlert("Tapped", e.SelectedItem + " row was tapped", "OK");
((ListView)sender).SelectedItem = null;
}
There is a better way to do it?
I want something like this without alphabet and search menu :
Take a look on this https://github.com/ricardoromo/Listview-Multiselect, I made this sample to simulate miltiselect listview.
You could try using XLabs checkbox or radioButton control, Here is a list of controls examples, you just need to download the package via NuGet Manager.
If you only need to check 1 item, in your tapped event make an action after item is tapped. Here's an example:
async void FriendListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var el = e.Item as ProfileItem;
SelectedItem = el;
if (e.Item != null)
{
await Navigation.PushAsync(new FriendProfile(el));
}
((ListView)sender).SelectedItem = null; // de-select the row
}
I've tried the Multiselection listview in xamarin forms and I got it working for both iOS and Android.
This took me some time so I wrote a blog about the entire prototype. You can find it here :
http://androidwithashray.blogspot.com/2018/03/multiselect-list-view-using-xamarin.html
The blog gives you full info on creating the Listview with the checkbox and selecting multiple items. Hope this helps!!
Related
On Page#1, i have a collectionview with text/string items list. If you tap a a item, it will get the current tapped text/string and send to Page#2. sound simple
issue I am having: if you tap on item#1, then it will send item#1 to page2, this part working fine. But on page#2, if you hit back button, and tap on item#1 again.. than nothing happens, it doesnt go to page#2
Fix: i think i need to somehow clear tap selection, and than send the item to page#2. but im not sure how to do this
On Page#1, i have a simple collectionview. Collection view contains text/string list
<CollectionView ItemsSource="{Binding MyListItem}"
SelectionMode="Single"
SelectionChanged="CollectionView_SelectionChanged">
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentView>
<!-- Body -->
<Grid Padding="0">
<Frame CornerRadius="3" BorderColor="#f2f4f5" HasShadow="True">
<StackLayout Orientation="Horizontal">
<Image Source="icon_about"
WidthRequest="25" />
<StackLayout VerticalOptions="Center">
<Label VerticalOptions="Center"
FontSize="16"
Text="{Binding .}" />
</StackLayout>
</StackLayout>
</Frame>
back end code to handle selection is:
private async void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var previous = e.PreviousSelection.FirstOrDefault();
var current = e.CurrentSelection.FirstOrDefault();
var route = $"{ nameof(Page2) }?URLCardType={current}";
await Shell.Current.GoToAsync(route);
//clear selection
((CollectionView)sender).SelectedItem = null;
}
Update ((CollectionView)sender).SelectedItem = null; fixed the issue of clearing selected item but CollectionView_SelectionChanged method is get run twice on single tap. why? this is all the code i have
#jason thanks, this worked for me. i just had to check if selection item is null than do nothing
private async void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var MyCollectionView = sender as CollectionView;
if (MyCollectionView.SelectedItem == null)
return;
var previous = e.PreviousSelection.FirstOrDefault();
var current = e.CurrentSelection.FirstOrDefault();
var route = $"{ nameof(Page2) }?URLCardType={current}";
await Shell.Current.GoToAsync(route);
//clear selection
MyCollectionView.SelectedItem = null;
}
Here is a simple solution that worked for me.
Setting to null did not do the trick, it was causing all kinds of
weird stuff.
private void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var itemselected = e.CurrentSelection[0] as ProductsItem;
if (itemselected != null)
Shell.Current.GoToAsync(nameof(SelectedProductPage));
//Setting the selected item to an emptyviewproperty after navigation
CollectionList.SelectedItem = SelectableItemsView.EmptyViewProperty;
}
In XAML & C# using Xamarin.Forms (iOS Project) I'm trying to create a gallery where the user can add photos to it. Currently I can show a list that does have the photo data binded to each entry because the user can click on an item in the list and the correct image will show up.. However I have not been successful in actually displaying a smaller version of the pictures in my FlowListView. I know it has to be something with the binding and I'm trying to grab the image uri from each object to display it but I'm still pretty new to this, especially to xaml and haven't been successful with this part yet.
If you could point me in the right direction that would be sweet!
Expected Result
To display images in 2 columns using FlowListView and FFImageLoading
Actual Result
I currently an able to display a 2 column list that has the right objects tied to each item but the only way I can actually see if anything is there is if I add a frame or add a text label to each item.
The user can click on each label currently and the correct image will show.
This is part of my TicketPageModel
private void AddItems()
{
public void UpdatePhotosData()
{
//get the notes and set the source for the list to them.
photos = NLTicketPhotos.Get(_ticket).OrderByDescending(x => x.PhotoCreatedAt).ToList();
}
foreach (var i in photos)
{
Items.Add(i);
}
}
private ObservableCollection<NLTicketPhoto> _items = new ObservableCollection<NLTicketPhoto>();
public ObservableCollection<NLTicketPhoto> Items
{
get
{
return _items;
}
set
{
if (_items != value)
{
_items = value;
OnPropertyChanged(nameof(Items));
}
}
}
My XAML
<flv:FlowListView FlowColumnCount="2" SeparatorVisibility="Default" HasUnevenRows="True" FlowItemTapped="OnImageTapped" FlowItemsSource="{Binding Items}">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<StackLayout Padding="10" Spacing="0" AutomationProperties.Name="Too Cool">
<ff:CachedImage Aspect="AspectFill" HeightRequest="30">
<ff:CachedImage.Source>
<UriImageSource Uri="{Binding Items.PhotoFileUrl}"/>
</ff:CachedImage.Source>
</ff:CachedImage>
<StackLayout Orientation="Horizontal" Padding="10" Spacing="0">
<Label HorizontalOptions="Fill" VerticalOptions="Fill" TextColor="Black" XAlign="Center" YAlign="Center" Text="Too Cool For School"/>
</StackLayout>
</StackLayout>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
My Code Behind
void OnImageTapped(object sender, ItemTappedEventArgs e)
{
NLTicketPhoto photo = (NLTicketPhoto)e.Item;
//listPhotos.SelectedItem = null; //deselect the item
switch (photo.PhotoFileType)
{
case "mov":
if (photo.PhotoIsOnServer)
{
Device.OpenUri(new Uri(photo.PhotoFileName));
}
else
{
//Only watch videos after sync
DisplayAlert("Video Unavailable", string.Format("This video will be viewable after it is synced to the server."), "OK");
}
break;
case "jpg":
//View image
NLPageViewPhoto preview = new NLPageViewPhoto(photo);
Navigation.PushAsync(preview);
break;
default:
DisplayAlert("Photo Unavailable", string.Format("The photo format .{0} is currently not viewable", photo.PhotoFileType), "OK");
break;
}
}
Once I implemented INotifyPropertyChanged to my NLTicketPhoto model and tweaked my PhotoUrl and PhotoDescription properties to use OnPropertyChanged() I was able to get my list to display properly.
Following this example helped me. https://www.c-sharpcorner.com/article/grid-view-in-xamarin-forms-using-flowlistview/
I am trying to figure out how to use the ScrollTo method that is available with list views. I believe I have the last two arguments correct, but am not quite sure if I have the first argument correct.
Here is the xaml with my list view for my view...
<ListView
x:Name="MyList"
Grid.Row="0"
Grid.RowSpan="4"
Grid.Column="0"
Grid.ColumnSpan="8"
ItemsSource="{Binding History}"
ItemTapped="OnItemTapped"
RowHeight="60">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Margin="8">
<Label
Text="{Binding MessageTitle}"
FontAttributes="Bold" />
<Label
Text="{Binding MessageContents}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And here is part of the code behind for that view where I am trying to use .ScrollTo
private void ScrollToBottomClicked(object sender, EventArgs e)
{
List.ScrollTo(MyList.ItemsSource, ScrollToPosition.End, true);
}
Here is the ItemSource, which is an observable collection in my view model
public ObservableCollection<HistoryMessage> History
{
get
{
return History;
}
set
{
History = value;
}
}
When the ScrollToBottom button is clicked I am wanting the list view display to jump the the most recent item added. Any help would be greatly appreciated!
For example:
In my code behind I have the following method, in my case is void because I only call when I load all the list
public void ScrollDown(HistoryMessage historyMessage)
{
List.ScrollTo(historyMessage, ScrollToPosition.End, false);
}
So, when all I get all the list I call this method
ScrollDown(History.LastOrDefault());
If you have a button and you want me to show you the closest item of the list, you should make a query to your API to see if there is any element added and later call this method and sent the lastOrDefault element.
If what you want is the first element you need change List.ScrollTo(historyMessage, ScrollToPosition.End, false) to List.ScrollTo(historyMessage, ScrollToPosition.Start, false) and sent de first element in your observable collection
I have built a app using Xamarin.Forms that I am displaying in Windows.
Requirement:
- tap on item in the ListView >
- the displayed value for the item changes in the UI
Here is the relevant xaml
NOTE: I have set the ListView.ItemsSource from code behind
<ListView.ItemTemplate >
<StackLayout VerticalOptions="Center" HorizontalOptions="Center" >
<Label Text="Passo dopo passo" VerticalOptions="Center" HorizontalOptions="Center" />
<Button Clicked="GetGroceries" Text="Get Groceries" ></Button>
<ListView x:Name="lvGroceries" ItemTapped="GroceryItemTapped" >
<ListView.ItemTemplate >
Here is the c# code behind, which runs fine when the item is tapped, but the UI does not change.
public ObservableCollection oc;
public void GroceryItemTapped (object o, ItemTappedEventArgs e )
{
if (e.Item == null ) {
return;
}
var g = ((GroceryItem)e.Item);
// oc is populated and exactly what I expect here
foreach (var gr in oc)
{
// the next line grabs the item I want
if (gr.GroceryId == g.GroceryId)
{
gr.GroceryName = "snot";
} } }
// when the above code runs, the data is already in oc and this code ran
lvGroceries.ItemsSource = oc;
Ideas?
You need to bind your ListView to your collection:
<ListView x:Name="lvGroceries" ItemsSource="{Binding oc}" ItemTapped="GroceryItemTapped" >
You also need to set your ContentPage.BindingContext. You also need to implement INotifyPropertyChanged and run OnPropertyChanged() anytime your collection is updated. Finally, you need to add to your collection by doing oc.Add() instead of reseting the ItemSource every time because that will overwrite your binding.
Highly suggest looking through the Xamarin Forms Binding Documentation.
I am building a small Windows Phone application which has a databound ListBox as a main control. DataTemplate of that ListBox is a databound ItemsControl element, which shows when a person taps on a ListBox element.
Currently, I am accessing it by traversing the visual tree of the application and referencing it in a list, and than getting the selected item through SelectedIndex property.
Is there a better or more effective way?
This one works currently, but I am afraid if it would stay effective in case of larger lists.
Thanks
Have you tried wiring the SelectionChanged event of the ListBox?
<ListBox ItemsSource="{Binding}" SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- ... -->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
With this in the code behind:
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox listBox = sender as ListBox;
// nothing selected? ignore
if (listBox.SelectedIndex != -1)
{
// something is selected
}
// unselect the item so if they press it again, it takes the selection
listBox.SelectedIndex = -1;
}
ListBoxItem item = this.lstItems.ItemContainerGenerator.ContainerFromIndex(yourIndex) as ListBoxItem;
Then you can use the VisualTreeHelper class to get the sub items
var containerBorder = VisualTreeHelper.GetChild(item, 0) as Border;
var contentControl = VisualTreeHelper.GetChild(containerBorder, 0);
var contentPresenter = VisualTreeHelper.GetChild(contentControl, 0);
var stackPanel = VisualTreeHelper.GetChild(contentPresenter, 0) as StackPanel; // Here the UIElement root type of your item template, say a stack panel for example.
var lblLineOne = stackPanel.Children[0] as TextBlock; // Child of stack panel
lblLineOne.Text = "Some Text"; // Updating the text.
Another option is to use services of the GestureServices class available in the WP7 Toolkit.
You'll need to add a GestureListner to the Root Element of your DataTemplate like so:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Controls:GestureService.GestureListener>
<Controls:GestureListener Tap="GestureListener_Tap" />
</Controls:GestureService.GestureListener>
<TextBlock x:Name="lblLineOne" Text="{Binding LineOne}" />
<TextBlock Text="{Binding LineTwo}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
And in the GestureListener_Tap event handler, you use this snippet.
private void GestureListener_Tap(object sender, GestureEventArgs e)
{
var itemTemplateRoot = sender as StackPanel;
var lbl1 = itemTemplateRoot.Children[0] as TextBlock;
MessageBox.Show(lbl1.Text);
}
I'm not sure how the GestureListner recognize internally the item being tapped but I guess that it uses the VisualTreeHelper, at least this method is more concise.