Home > Software design >  Xamarin show one item from DataTemplate
Xamarin show one item from DataTemplate

Time:10-23

I'm trying to show one item with a DataTemplate in my Xamarin app, what XAML control can I use?

In my case I am building a meetings app and I am trying to show the meeting's owner.
I tried using CollectionView (UserTemplate is a DataTemplate):

<CollectionView
            ItemsSource="{Binding Meeting.Owner}"
            ItemTemplate="{StaticResource UserTemplate}"/>

but this didn't show anything because CollectionView supports only a list of items (and it's really worked when i tried to use a list).
What alternative can I use for a single item?

UserTemplate XAML:

<DataTemplate x:Key="UserTemplate">
    <Grid Padding="10"
          RowDefinitions="Auto,Auto"
          ColumnDefinitions="Auto,Auto">
        <Image Grid.RowSpan="2" 
               Source="{Binding IconURL}" 
               Aspect="AspectFill"
               HeightRequest="60" 
               WidthRequest="60" />
        <Label Grid.Column="1" 
               Text="{Binding Name}" 
               FontAttributes="Bold" />
        <Label Grid.Row="1"
               Grid.Column="1" 
               Text="{Binding age}"
               FontAttributes="Italic" 
               VerticalOptions="End" />
    </Grid>
</DataTemplate>

CodePudding user response:

If your goal is to display the details of a single property (User) on another property (Meeting), it is not necessary to use a DataTemplate.

Consider these models:

using Xamarin.Forms;

namespace BindSubView
{
    public class Meeting : BindableObject
    {
        public string Description { get; set; } = "Description of meeting";
        public User User { get; set; } = new User();
    }

    public class User : BindableObject
    {
        public string Name { get; set; } = "Test User";
        public string SomeProperty { get; set; } = "Test SomeProperty";
    }
}

: BindableObject is optional; it is for use (later in development) of the convenience method OnPropertyChanged(nameof(SomeProperty));

And this view model:

public class MainPageViewModel : BindableObject
{
    public Meeting Meeting { get; set; } = new Meeting();
}

Two possible approaches.

-1. Refer directly to "nested" properties. E.g. {Binding Meeting.User.Name}:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:BindSubView"
             x:Class="BindSubView.MainPage">
    <ContentPage.BindingContext>
        <local:MainPageViewModel/>
    </ContentPage.BindingContext>
    <StackLayout>
        <Label Text="{Binding Meeting.Description}"/>
        <StackLayout>
            <Label Text="{Binding Meeting.User.Name}"/>
            <Label Text="{Binding Meeting.User.SomeProperty}"/>
        </StackLayout>
    </StackLayout>
</ContentPage>

OR 2. Set a different "BindingContext" on parts of the XAML.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:BindSubView"
             x:Class="BindSubView.MainPage">
    <ContentPage.BindingContext>
        <local:MainPageViewModel/>
    </ContentPage.BindingContext>
    <!-- "Meeting" refers to a Property on the enclosing BindingContext. Here it means "MainPageViewModel.Meeting". -->
    <StackLayout BindingContext="{Binding Meeting}">
        <Label Text="{Binding Description}"/>
        <!-- "User" refers to a Property on the enclosing BindingContext. Here it means "MainPageViewModel.Meeting.User". -->
        <StackLayout BindingContext="{Binding User}">
            <Label Text="{Binding Name}"/>
            <Label Text="{Binding SomeProperty}"/>
        </StackLayout>
    </StackLayout>
</ContentPage>

These two XAML snippets behave identically. Both display:

Description of meeting

Test User

Test SomeProperty

CodePudding user response:

I know that this may not be a perfect solution but maybe it will help you.

Instead of a Template, you could use a ContentView for displaying your Data. the XML for that would look almost similar to your Template.

XAML:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:models="clr-namespace:TestAppNew.Models"
             x:Class="TestAppNew.Views.UserView"
             x:Name="View">

    <Frame BindingContext="{Binding Source={x:Reference View}, Path=User}" 
           x:DataType="models:User">
        <Grid Padding="10"
              RowDefinitions="Auto,Auto"
              ColumnDefinitions="Auto,Auto">

            <Label Grid.Column="1" 
                   Text="{Binding Name}" 
                   FontAttributes="Bold" />

            <Label Grid.Row="1"
                   Grid.Column="1" 
                   Text="{Binding age}"
                   FontAttributes="Italic" 
                   VerticalOptions="End" />
        </Grid>
    </Frame>
</ContentView>

Code Behind:

    public partial class UserView : ContentView, INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;

            changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion

        public User User
        {
            get => (User)GetValue(UserProperty);
            set => SetValue(UserProperty, value);
        }

        public static readonly BindableProperty UserProperty =
            BindableProperty.Create("User", typeof(User), typeof(UserView), new User(),
                propertyChanged: (bindable, oldVal , newVal) => ((UserView)bindable).OnPropertyChanged(nameof(User)));


        public UserView()
        {
            InitializeComponent();
        }
    }

Now you are able to use this View in your Code.

Single Items:

<views:UserView User="{Binding Meeting.Owner}" />

Or Multiple:

<CollectionView ItemsSource="{Binding Meetings}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <views:UserView User="{Binding Owner}" />
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Without doing further researches, I think you should be able to Wrap this View in an extra DataTemplate so, that you could still access the direct ItemTemplate Attribute of a ListView or CollectionView

  • Related