Hello,
Is it possible to nest two ListView each with a different DataSource?
Can you give an example, please?
1 Answer, 1 is accepted
Hello,
I have created a sample project with two ListView controls The second is added inside the first ListView ItemTemplate. In the project, there are two different DataSources and two collections. Download the provided example, use it as a base and extend it further per your exact scenario.
Regarding to the question for nesting ListViews - It Is possible to add ListView inside ListView, but it is not recommended and let me explain why: ListView control has an internal scrolling mechanism, and you should avoid nesting scrolling controls. There is a warning in the .NET Maui ScrollView documentation: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/scrollview?view=net-maui-7.0
ScrollView objects should not be nested. In addition, ScrollView objects should not be nested with other controls that provide scrolling, such as CollectionView, ListView, and WebView.
The same is valid for Telerik .NET MAUI controls that have internal scrolling, controls like: DataGrid, ListView, DataForm, ItemsControl.
Regards,
Maria
Progress Telerik
Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.
Thanks for the help.
The problem that I am presenting is when I want to bind commands from the detail or nested list, it does not recognize them.
<telerik:RadListView x:Name="SampleList" ItemsSource="{Binding Coleccion}" SelectionMode="None">
<telerik:RadListView.ItemTemplate>
<DataTemplate>
<telerik:ListViewTemplateCell>
<telerik:ListViewTemplateCell.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Text="{Binding NameStore}" />
<Label Grid.Row="1" Text="{Binding Description}" />
<telerik:RadListView x:Name="DetailSampleList" Grid.Row="2" ItemsSource="{Binding Details}" SelectionMode="None">
<telerik:RadListView.ItemTemplate>
<DataTemplate>
<telerik:ListViewTemplateCell>
<telerik:ListViewTemplateCell.View>
<Grid>
<Label Text="{Binding NameMark}" />
</Grid>
</telerik:ListViewTemplateCell.View>
</telerik:ListViewTemplateCell>
</DataTemplate>
</telerik:RadListView.ItemTemplate>
<telerik:RadListView.Commands>
<telerik:ListViewUserCommand Id="ItemTap" Command="{Binding DetailItemTapCommand}" />
</telerik:RadListView.Commands>
</telerik:RadListView>
</Grid>
</telerik:ListViewTemplateCell.View>
</telerik:ListViewTemplateCell>
</DataTemplate>
</telerik:RadListView.ItemTemplate>
<telerik:RadListView.Commands>
<telerik:ListViewUserCommand Id="ItemTap" Command="{Binding ColeccionItemTapCommand}" />
</telerik:RadListView.Commands>
</telerik:RadListView>
This is the ViewModel
public partial class NestedListDemoViewModel : ObservableObject
{
public NestedListDemoViewModel()
{
ObservableCollection<DetailModel> list = new ObservableCollection<DetailModel>();
list.Add(new DetailModel { DaysLeft = 10, NameMark = "MARK1", PerfectStore = 0, StatusMark = "PENDING" });
list.Add(new DetailModel { DaysLeft = 5, NameMark = "MARK2", PerfectStore = 0, StatusMark = "PENDING" });
list.Add(new DetailModel { DaysLeft = 3, NameMark = "MARK3", PerfectStore = 1, StatusMark = "PENDING" });
list.Add(new DetailModel { DaysLeft = 2, NameMark = "MARK4", PerfectStore = 1, StatusMark = "PENDING" });
ObservableCollection<DetailModel> list2 = new ObservableCollection<DetailModel>();
list2.Add(new DetailModel { DaysLeft = 3, NameMark = "MARK1", PerfectStore = 0, StatusMark = "PENDING" });
list2.Add(new DetailModel { DaysLeft = 6, NameMark = "MARK2", PerfectStore = 0, StatusMark = "PENDING" });
list2.Add(new DetailModel { DaysLeft = 9, NameMark = "MARK5", PerfectStore = 0, StatusMark = "PENDING" });
_Coleccion = new ObservableCollection<SampleModel> {
new SampleModel { Description = "Description of XXX", Details = list, HasChilds = true, NameStore = "XXX" },
new SampleModel { Description = "Description of YYY", Details = list2, HasChilds = true, NameStore = "YYY"}
};
}
[ObservableProperty]
ObservableCollection<SampleModel> _Coleccion;
[RelayCommand]
void DetailItemTap(ItemTapCommandContext context)
{
var item = context.Item;
}
[RelayCommand]
void ColeccionItemTap(ItemTapCommandContext context)
{
var item = context.Item;
}
}
DetailItemTap is not called on the viewModel, can you help me please.
Greetings!!
Hi Comercializadora Paxia, this is just one of the problems you're going to run into, which is why Maria warned you against it. You have competing commands, Tap for the top control and a Tap for the nested control.
Let's step back and think about this in terms of layers. If you have Tap gesture commands setup for both ListVews, how would the top ListView know that the user's gesture is for the inner ListView?
Here's a diagram to help explain the topic:
There is a solution to this known as chaining (scroll viewers use chaining for example), but this is not something available for tap gestures.
This is the same conceptual black hole anywhere on any platform, when using nested gesture recognition systems, only one of them is going to get the gesture.
Suggestions
If this is what you want to achieve, I recommend using different control for the inner list. A CollectionView with gestures disabled and only use events, or maybe RadItemsControl and use a Button inside the child's ItemTemplate
For example, you can do this with an EventToCommand behavior, and set the normal content to InputTransparent=True:
<DataTemplate>
<Grid>
<!-- This Button is invisible ot the user, it's only here to trigger the clicked event-->
<Button BackgroundColor="Transparent">
<Button.Behaviors>
<behaviors:EventToCommandBehavior EventName="Clicked"
Command="{Binding SomeCommand}"
CommandParameter="{Binding}"/>
</Button.Behaviors>
</Button>
<!-- The normal content has InputTransparent=True so the user's click/tap reaches the button -->
<StackLayout InputTransparent="True">
<Label Text="Your normal content, use InputTransparent to True to allow the Button click event"
InputTransparent="True"></Label>
</StackLayout>
</Grid>
</DataTemplate>
Ultimately, what you're trying to do currently is not a supported scenario. Toi achieves it, will take some experimentation with workarounds. If you want an official control with hierarchical this will need to be done with a control that supports it, like a TreeView (this isn't in MAUI yet, but we're working on it)
We're in the same boat, having a model like:
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string CustomerId { get; set; }
public List<Order> Orders { get; set; }
public class Order
{
public string OrderId { get; set; }
public DateTime OrderDate { get; set; }
public decimal OrderTotal { get; set; }
}
}
I thought I'd try using ListView grouping and the GroupHeaderTapCommand so the data was flattened to:
public class CustomerOrder : INotifyPropertyChanged
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string CustomerId { get; set; }
public string OrderId { get; set; }
public DateTime OrderDate { get; set; }
public decimal OrderTotal { get; set; }
}
Hi Neil,
In general, the BindingContext of the group header template provides all the information about the items in the corresponding group - you can get the group key, Items collection and level - please check here: ListView: Group Header Template.
Other than that, I'd ask you to give us more details on the exact requirement you have - can you send, for example, an image showing the expected result with presenting the data in a ListView control? In this way I can provide more precise instructions on how it can be achieved.
In addition, recently we released a new .NET MAUI CollectionView control which provides the functionality of the ListView with some enhancements and improved performance. The CollectionView provides grouping and customizable group headers as well.
I look forward to your reply.
Hi Yana. I've attached a runnable project. Please locate the "Show Customer Name in header" comment in MainPage.xaml to see what I'm after. The real solution has to work on iOS and has to handle taps on the Customer section and for each order and support load on demand. It also has to show eight properties from the customer model.
We had the presentation and LOD parts working with nested listviews but then I found this thread which says, "don't do that".
Hi Neil,
Thank you for sharing the runnable app, it was of great help.
The GroupHeadeContext (which is the BindingContext of the group header) provides Items property which holds a collection of the items of that group. Since all the items are grouped by CustomerId, that suggests they have the same Customer FirstName and LastName. So, in order to get them in the GroupHeader template, you can use the following approach:
<telerik:RadListView.GroupHeaderTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<!--Show Customer Name in header-->
<Label Text="{Binding Items[0].FirstName}" FontAttributes="Bold" Padding="10,0" />
<Label Text="{Binding Items[0].LastName}" FontAttributes="Bold" Padding="10,0" />
</StackLayout>
</DataTemplate>
</telerik:RadListView.GroupHeaderTemplate>
Please find attached the updated app for a reference.
On a side note, I'd recommend you migrate the ListView to the CollectionView - the grouping functionality is the same, some small changes will be needed regarding commands. still, the CollectionView provides more flexibility and improved performance. You can take a look at the following topic on the matter: Migrating the Telerik .NET MAUI ListView to CollectionView.
I remain at your disposal if you have any additional questions on this.
I did look into implementing the CollectionView control and it looks like we might need to stick with ListView until this is implemented: https://feedback.telerik.com/maui/1658176-collectionview-add-cancellation-support-for-group-expand-collapse
Seeing the group collapse (even briefly) when the header is tapped might confuse users.
Hi Neil,
Indeed, this is currently not supported in the CollectionView, I've updated the internal item with the additional information you provided.