How can I pass a ViewCell into custom control?

ReedReed Member ✭✭
edited January 25 in Xamarin.Forms

I'm making a custom control and how can I pass other View into it?

In my case I want to pass in a custom ViewCell to my custom ListView so I could re-use this custom control with different item sources and their proper ViewCells:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Foobar.Base.Views.LazyListView">
    <ContentView.Content>
        <StackLayout>
            <Label Text ="Custom ListView"></Label>
            <CustomListView ItemsSource="{Binding Items}">
                <CustomListView .ItemTemplate>
                   ** <!--Render a custom ViewCell that was passed in to the custom control-->**
                </CustomListView.ItemTemplate>
            </CustomListView >
        </StackLayout>
    </ContentView.Content>
</ContentView>

Should I use something like x:Arguments for it?

Posts

  • NMackayNMackay GBInsider, University mod

    @Reed said:
    I'm making a custom control and how can I pass other View into it?

    In my case I want to pass in a custom ViewCell to my custom ListView so I could re-use this custom control with different item sources and their proper ViewCells:


    <ContentView.Content>




    ** **
    </CustomListView.ItemTemplate>


    </ContentView.Content>

    Should I use something like x:Arguments for it?

    Maybe ControlTemplates.
    https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/control-templates/introduction

  • JoeMankeJoeManke USMember ✭✭✭✭✭
    edited January 25
    <ListView.ItemTemplate>
        <DataTemplate>
            <CustomCell/>
        </DataTemplate>
    </ListView.ItemTemplate>
    
  • ReedReed Member ✭✭
    edited January 25

    @JoeManke said:

    <ListView.ItemTemplate>
        <DataTemplate>
            <CustomCell/>
        </DataTemplate>
    </ListView.ItemTemplate>
    

    Thanks for the answer, but I don't want to couple my custom control with specific CustomCell. What I'm looking for is a way of passing the cell itself to my LazyListView custom control. An example usage of the custom control could look like this:

    <LazyListView Items="{Binding Items}" >
        <!--Pass in a custom ViewCell-->
    </LazyListView>
    
  • JoeMankeJoeManke USMember ✭✭✭✭✭

    Make a DataTemplate BindableProperty the same way you're doing the Items.

  • ReedReed Member ✭✭
    edited January 27

    @NMackay Thanks for the answer, I've tried your suggested approach, but I have one issue.

    The elements that I pass to the template are bound too early. Because of BindingContext inheritance, element is bound to the parent context, but I want it to bind to the type of ItemsSource when the element is being displayed by ContentPresenter.

    <ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="TabbedPage.CustomListView"
                 x:Name="this"
                 ControlTemplate="{StaticResource Template}">
        <ContentView.Resources>
            <ResourceDictionary>
                <ControlTemplate x:Key="Template">
                    <StackLayout>
                        <ListView ItemsSource="{Binding Source={x:Reference this}, Path=Items}">
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <ViewCell>
                                        <ContentPresenter></ContentPresenter>
                                    </ViewCell>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                    </StackLayout>
                </ControlTemplate>
            </ResourceDictionary>
        </ContentView.Resources>
        <ContentView.Content >
            <Label Text="{Binding Name}"></Label>
        </ContentView.Content>
    </ContentView>
    

    Here's how I would like it to behave (notice the comments):

    <?xml version="1.0" encoding="UTF-8"?>
    <ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="TabbedPage.CustomListView"
                 x:Name="this"
                 ControlTemplate="{StaticResource Template}">
        <ContentView.Resources>
            <ResourceDictionary>
                <ControlTemplate x:Key="Template">
                    <StackLayout>
                        <ListView ItemsSource="{Binding Source={x:Reference this}, Path=Items}">
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <ViewCell>
                                        <!--<ContentPresenter></ContentPresenter>-->
                                        <!--<Label Text="{Binding Name}"></Label> ContentPresenter is replaced by
                                        the element and the element is bound to the correct context-->
                                    </ViewCell>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                    </StackLayout>
                </ControlTemplate>
            </ResourceDictionary>
        </ContentView.Resources>
        <ContentView.Content >
            <!--<Label Text="{Binding Name}"></Label> I pass in the element-->
        </ContentView.Content>
    </ContentView>
    
  • ReedReed Member ✭✭
    edited January 27

    @JoeManke said:
    Make a DataTemplate BindableProperty the same way you're doing the Items.

    Could you provide an example of how it should be done?

    I've added DataTemplate as a BindableProperty to the custom control. How do I represent and bind it in my custom control's XAML?

        public static readonly BindableProperty CustomDataTemplateProperty =
            BindableProperty.Create(nameof(CustomDataTemplate), typeof(DataTemplate),
                typeof(CustomListView));
    
        public DataTemplate CustomDataTemplate
        {
            get { return (DataTemplate)GetValue(CustomDataTemplateProperty); }
            set { SetValue(CustomDataTemplateProperty, value); }
        }
    
  • JoeMankeJoeManke USMember ✭✭✭✭✭

    The BindableProperty you've written is right, here is the usage:

    LazyListView.xaml

    <?xml version="1.0" encoding="UTF-8"?>
    <ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="TabbedPage.CustomListView"
                 x:Name="this">
        <StackLayout>
            <ListView 
                ItemsSource="{Binding Source={x:Reference this}, Path=Items}" 
                ItemTemplate="{Binding Source={x:Reference this}, Path=CustomDataTemplate}"/>
        </StackLayout>
    </ContentView>
    

    Using the LazyListView:

    <LazyListView Items="{Binding Items}">
        <LazyListView.CustomDataTemplate>
            <DataTemplate>
                <ViewCell>
                    <Label Text="{Binding Name}"/>
                </ViewCell>
            </DataTemplate>
        </LazyListView.CustomDataTemplate>
    </LazyListView>
    
  • ReedReed Member ✭✭

    Thanks for your time, this solves my issue. I still have one quick question before marking your reply as the answer.

    Before you've answered, I've extracted my DataTemplate into another view, declared it in my resource dictionary and used it as a DynamicResource

    Would this also be correct?

        <ContentPage.Resources>
            <ResourceDictionary>
                <views:OrderListItem x:Key="OrderListItem"></views:OrderListItem>
            </ResourceDictionary>
        </ContentPage.Resources>
        ...
        <LazyListView Items="{Binding Items}" CustomDataTemplate="{DynamicResource OrderListItem}">
        </LazyListView>
    
  • JoeMankeJoeManke USMember ✭✭✭✭✭

    The resource needs to be a DataTemplate, and I don't think this is a use case for a dynamic resource.

    <ContentPage.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="OrderListItemTemplate">
                <views:OrderListItem/>
            </DataTemplate>
        </ResourceDictionary>
    </ContentPage.Resources>
    ...
    <LazyListView Items="{Binding Items}" CustomDataTemplate="{StaticResource OrderListItemTemplate}"/>
    
    
Sign In or Register to comment.