A view similar to a ListView without baked in scrolling, or a TableView with an ItemsSource. The view should allow for an instance of itself to be created as a child element as well, for list within list functionality.
I believe that the additional RepeaterView would have a subset of the ListView API. The repeater would not have any of the pull-to-refresh, context actions, nor selection functionality. There would also not be any baked in scrolling, behaving like the TableView in this regard.
This is what I have been using, with the only problem thus far being that I wasn't able to apply a tap gesture to an image. This problem probably applies to gestures in general
using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using Xamarin.Forms; public delegate void RepeaterViewItemAddedEventHandler(object sender, RepeaterViewItemAddedEventArgs args); // in lieu of an actual Xamarin Forms ItemsControl, this is a heavily modified version of code from public class RepeaterView : StackLayout { public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create( "ItemSource", typeof(IEnumerable), typeof(RepeaterView), new List<object>(), BindingMode.OneWay, propertyChanged: ItemsChanged); public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create( "ItemTemplate", typeof(DataTemplate), typeof(RepeaterView), default(DataTemplate)); public event RepeaterViewItemAddedEventHandler ItemCreated; public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } public DataTemplate ItemTemplate { get { return (DataTemplate)GetValue(ItemTemplateProperty); } set { SetValue(ItemTemplateProperty, value); } } private static void ItemsChanged(BindableObject bindable, object oldValue, object newValue) { try { var control = (RepeaterView)bindable; var oldObservableCollection = oldValue as INotifyCollectionChanged; if (oldObservableCollection != null) { oldObservableCollection.CollectionChanged -= control.OnItemsSourceCollectionChanged; } var newObservableCollection = newValue as INotifyCollectionChanged; if (newObservableCollection != null) { newObservableCollection.CollectionChanged += control.OnItemsSourceCollectionChanged; } control.Children.Clear(); if (newValue != null) { foreach (var item in (IEnumerable)newValue) { var view = control.CreateChildViewFor(item); control.Children.Add(view); control.OnItemCreated(view); } } control.UpdateChildrenLayout(); control.InvalidateLayout(); }catch(Exception e){ throw; } } protected virtual void OnItemCreated(View view) => this.ItemCreated?.Invoke(this, new RepeaterViewItemAddedEventArgs(view, view.BindingContext)); private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { var invalidate = false; if (e.OldItems != null) { this.Children.RemoveAt(e.OldStartingIndex); invalidate = true; } if (e.NewItems != null) { for (var i = 0; i < e.NewItems.Count; ++i) { var item = e.NewItems[i]; var view = this.CreateChildViewFor(item); this.Children.Insert(i + e.NewStartingIndex, view); OnItemCreated(view); } invalidate = true; } if (invalidate) { this.UpdateChildrenLayout(); this.InvalidateLayout(); } } private View CreateChildViewFor(object item) { this.ItemTemplate.SetValue(BindableObject.BindingContextProperty, item); return (View)this.ItemTemplate.CreateContent(); } } public class RepeaterViewItemAddedEventArgs : EventArgs { private readonly View view; private readonly object model; public RepeaterViewItemAddedEventArgs(View view, object model) { this.view = view; this.model = model; } public View View => this.view; public object Model => this.model; }
This is what I would like to do but the gesture recognizer is not working
<controls:RepeaterView <controls:RepeaterView.ItemTemplate> <DataTemplate> <ContentView Padding="20,10,20,10" BackgroundColor="{Binding AgeColor}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Label Grid. <ContentView Grid. <Image Source="GreenStar.png" WidthRequest="40" HeightRequest="40" IsVisible="{Binding Acknowledged}"> <Image.GestureRecognizers> <TapGestureRecognizer Command="{Binding UnacknowledgeEventCommand}" CommandParameter="{Binding .}" /> </Image.GestureRecognizers> </Image> <Image Source="GreyStar.png" WidthRequest="40" HeightRequest="40" IsVisible="{Binding Unacknowledged}"> <Image.GestureRecognizers> <TapGestureRecognizer Command="{Binding AcknowledgeEventCommand}" CommandParameter="{Binding .}" /> </Image.GestureRecognizers> </Image> </ContentView> </Grid> </ContentView> </DataTemplate> </controls:RepeaterView.ItemTemplate> </controls:RepeaterView>
This is useful in highly data driven apps. When using an MVVM pattern the RepeaterView pattern is preferable over the TableView because manually adding items to a table view requires code in the view, which the MVVM pattern tries to avoid.
I assume this would be like a bindable stackpanel with a datatemplate selector? how would you expect to use this?
I currently do this for a dynamic grid layout for menu controls for people with fat fingers where I put large squares with icons and text as menu tiles and handle touch input (colouring on touch etc).
Please refine your proposal.
Propose an API for your repeaterview, explain, or draw, what it should look like, and give hints on how you think they should be implemented on each platform...
@StephaneDelcroix Is there anything more or different that you would like to see from what I added?
@MarkRadcliffe How is what your doing different that what you see from my control and example?
I think I just realized why my bindings aren't working. I'm trying to bind to the parent binding context in the item binding context
I added the commands to the item and assigned the parent context commands to the item commands but the parent action methods are not being triggered
Ok I have my gesture recognizing working correctly now. In addition to initially trying to reference the command which was in the wrong context, I had the wrong class type referenced in the command.
I still believe that this control would be helpful for people coming into the Xamarin Forms environment, or at very least offer it as a recipe in the Forms guides documentation
I hadn't seen your control example by that stage, but looks like that is what you currently do. Just keep in mind that your gesture recognizer may not work if you put this repeater view in a scroll view or in some other types of content control. The way I got around that (my menu also can be in a scrollview) is I made a "smart scroll view" that passes gesutres using interfaces if a click occurs it finds the control in that space and sends the on press down and up, tapped etc actions to it through to any control that handles that interface. Making it quite easy to create presses/touches on anything in that view through a basic subclassing of the type. Do you expect this control to scroll or work in a scrollview??
@MarkRadcliffe Thank you so much for your insight. I had not reached that cross roads yet. Are you referring to using the MessageCenter and subscribing to a particular interface that way?
@JoshuaRusso Not quite, I created a renderer for a "Smart Render", I did this from both a page and a scrollview. They are able to find the object that is being "pressed" based on the x and y coordinates and send those events to the child control if it implements IPressHander (just a basic interface). So you subclass the controls your want to have these events come through for on your repeater view. I was using box views as my buttons in my menu so I wanted them to change color on touch. Note in the scroll view you take the X and Y of the page but also the X and Y of the scroll point together to find the correct control.
Interesting, would this also be required on the Android side of things?
Yea, you do one renderer per platform, I have done one for each platform. there isn't much difference between them.
i.e you swap out the override functions in iOS with this for smart page
And this for SmartScrollView
This is getting off topic now though..
FYI on the android one you would want to seperate out the end and cancel to their respective functions in the interface, I had no need to so was lazy.
I'm not sure how off topic this really is. I would expect that a repeater would need to be allowable in a ScrollView. That just seems like something people would expect from an out-of-the-box control, and even if this is added as a recipe in the documentation scroll-ability is something that needs to be spoken to
Well the repeater would work in a scrollview anyway, but touch interactions and gestures become a bit harder as touch in a scrollview should mostly be for scrolling the view when it is a touch screen. same reason buttons dont really work in list views etc.
Is it possible that the touch interactions are not a concern anymore? I just created a contrived example with my image containing a tap gesture and made sure it repeated enough to scroll the content. The tap still worked without adding any special renderer
I have created a similar control before, however it required a column property, as I was using it more like a bindable Grid. But considering its incredibly close to this proposal, it might be worth adding. However a control that handles both might be an issue since StackLayout and Grid use different layout calculation methods. Unsure of performance implication of extending from either.
I love the idea of a bindable Grid! They're both are valid in different contexts. I've been using grid's within my StackLayout based repeater and it works, but I have contemplated making a bindable Grid as well. Unless I'm missing something I can't really see why there would be a performance hit. The output is generated when the page is built and then done. You have to be aware of the lack of virtualization, but the utility for short lists far outweighs the dangers of a developer trying to stuff too much into one.
@JoshuaRusso We have a BindableStackLayout and a BindableGrid which we have developed and use internally in our applications. All are built in PCL and do everything you are asking for above with no renderers required.
Let me know if you are interested. Might be looking at open sourcing some of these controls in the future so they can be improved by the wider community.
@Velocity Absolutely. I'm always interested in how others have solved interesting problems
Certain guestures work like the tap guesture. in the end it depends what type of input you are wanting. I.e if you want press down to change color and press up to trigger/change color back etc. The problem I guess stems from the lack of built in guesture support.
LEt's call it ItemsControl ?
@rmarinho Sounds good to me. I would just love to see it, what ever it ends up being called. This control is the linchpin to my app. The detail of the my objects are filled with lists of alternate IPs, passwords, contacts from email addresses to phone numbers and addresses.
I have also made a very similar control (because sometimes I just don't need all the
ListViewextra's). It seems like there are quite a few developers who could make use of an
ItemsControl.
ItemsControl, RepeaterView, CollectionView, WrapLayout.
Are people thinking of a control similar to the UICollectionView / RecyclerView? Or would this be different?
Here's what it a CollectionView could do.
@JoshuaRusso @MichaelRidland @rmarinho
Please see our BindableStackLayout control which is in its simplest form a bindable version of StackLayout.
We have used this extensively where there is a need for a simple, no-frills bindable StackLayout.
^Note: BindableStackLayout is not a replacement for a virtualized layout such as a ListView or a collection view.
It is designed to work as a simple repeater control for specific UI applications.
We are happy to contribute this to the Xamarin.Forms Evolution initiative.
@MichaelRidland It all boils down to a view that's bindable to a list of objects, similar to a ListView without the baked in scrolling of the ListView. This allows for the display of lists within lists, multiple lists on a page, and lists easily mixed with non-list based views.
I would say @MichaelRidland is pointing at the type of control that is badly needed in XF. While I realize we're talking a native iOS control the functionality of the UICollectionView is awesome from being able to scroll horizontally, vertically or not at all, having 1:n columns/rows. If you add that with DataTemplates like we're already used to for the ListView so you can create a custom feel per cell based on the binding context for the cell... and of course make it work on every platform... you'd have a lot of happy developers.
@DanSiegel @MichaelRidland Good suggestions, I have proposed this as a separate idea here.
Let's move any further discussion of a virtualized collection view into the new thread.
> While Forms should fully support the native controls as much as possible, there's a gap in functionality with the native controls that could be filled. Lists within lists are a powerful and intuitive concept for highly data driven apps. Virtualization is not always required and doesn't always make sense if you have a collection of small lists.
Exactly, which is the very reason we designed BindableStackLayout.
Virtualization is not always required and this simply provides bindable capability to the already great StackLayout.
Ok gotcha. I understand the intention of your post now and I completely agree
@rmarinho OK looks like we have a good idea of what this control aka "ItemsControl"needs to do.
Happy to volunteer a PR based on our BindableStackLayout if this feature request is considered as approved.
This control is built entirely in PCL and is a simple, solid extension to StackLayout that offers a bindable item source and templating. No hacks or renderers are required for gesture recognisers to work either! See above for more details.
Hello,
the lack of this control is really annoying as it is essential in most of app.
So few months ago i decided to implement that kind of control.
But recently ive definitively switched to as im not comforable with layouts calculations.
The author makes a good job and his controls answer a lot of dilemmas.
Probably this control could be part of Xamarin forms controls as Xamarin team did with AlexRainman CarouselView.
This is only a suggestion that exposes existing controls already available on Nuget..
thanks @Velocity. I've already created it, many times over, with each project, but just thought it would be handy to actually put in the XF framework.
I don't think you clearly defined what the problem is.
Based on my experience a good reason why sometimes
ListViewis not what I need is because I don't want items to be selectable. I just want a control which stacks items. Another reason might be
ListViewis kind of heavy from a performance point of view.
TableView has at least two inconveniences: 1) it doesn't have
ItemsSourcebindable and 2) it doesn't have an
ItemTemplateproperty.
I think it was really designed for static items, like settings or a menu, albeit there are many scenarios where items are not really that static, for example when you display the app settings. you want a setting to appear only if user checked another setting. This is a UI concern and it should be resolved in the Page code behind, without bindable items.
In Windows XAML we use
ItemsControlfor this purpose. So I guess that's why many people suggested an
ItemsControl. It would be really great to have it. But, since it's called ItemsControl I really want it follow same pattern like in Windows XAML. It should support using a
Layouttemplate (in Windows XAML it's
Panel) to layout the items, by having a
ItemsLayoutproperty (similar to the
ItemsPanelproperty in Windows XAML). This opens a lot of very cool possibilities. Anyone who worked enough with Windows XAML technologies knows what I'm talking about.
One "issue" (not an issue maybe, but just something to think about) I see with having an
ItemsControlis the way it actually renders on the native platform.
To understand what I mean, let's take the example of the existing
StackLayout. The
StackLayoutdoesn't have a renderer (it does, but it's a "generic" one), the layout of the items is implemented right in Xamarin Forms. It doesn't have a renderer which uses a
UIStackViewon iOS , or a
LinearLayouton Android, or a
StackLayouton UWP, or a
NSStackViewon MacOS. Today all platforms have a control which stacks items.
I wonder, from the speed point of view, how would the layout process done by Xamarin Forms compare to using the native stack controls? Is it negligible?
I completely agree that this (and Forms in general) should leverage as much native functionality as possible