2
votes

I am creating a subclass of ContentView, UserPostView to use as a data template in a ListView. I dynamically add items to the ItemSource of ListView by creating new instances of UserPostView.

<Image x:Name="PictureView" Source="Logo" BackgroundColor="#ddd" />
<Label x:Name="NameView" Text="Name" TextColor="#444" FontAttributes="Bold" Grid.Row="0" Grid.Column="1" Margin="10" />
<Label x:Name="LocationNameView" Text="Location" TextColor="#666" Grid.Row="0" Grid.Column="2" YAlign="Center" />
<Label x:Name="DeptNameView" Text="Dept" TextColor="#666" Grid.Row="0" Grid.Column="3" YAlign="Center" />

and in code:

using System;
using System.Collections.Generic;
using Xamarin.Forms;

namespace CMCityClient.Views
{
    public partial class UserPostView : ContentView
    {

        public ImageSource Picture
        {
            get { return PictureView.Source; }
            set { PictureView.Source = value; }
        }

        public string PostOwner {
            get { return NameView.Text; }
            set { NameView.Text = value; }
        }

        public string PostOwnerLocation {
            get { return LocationNameView.Text; }
            set { LocationNameView.Text = value; }
        }

        // ...

        // ...

        public UserPostView(ImageSource picture, string owner, string loc, string dept, string text) {
            InitializeComponent();

            Picture = picture;
            PostOwner = owner;
            PostOwnerLocation = loc;
            PostOwnerDept = dept;
            PostText = text;
        }

        public UserPostView(ImageSource picture, string owner, string loc, string dept, string text, ImageSource image) {
            InitializeComponent();

            Picture = picture;
            PostOwner = owner;
            PostOwnerLocation = loc;
            PostOwnerDept = dept;
            PostText = text;
            PostImage = image;
        }
    }
}

but when I use it in my ListView, it will not show the passed data, it shows the original data instead. (Text="Name"...)

public TimelinePage()
{
    InitializeComponent();

    ImageSource pic = ImageSource.FromUri(new Uri("https://tse1.mm.bing.net/th?id=OIP.acchQ02P8HmrmwbjCTwG5AHaHa&pid=Api"));
    timeline.ItemsSource = new UserPostView[]{
        new UserPostView(ImageSource.FromResource("Logo"), "Kevin Wang", "SHDR", "AOP", "hey"),
        new UserPostView(pic, "Lily", "SHDR", "ENT", "good"),
        new UserPostView(pic, "Zhang Shuo", "FRDL", "ENT", "Salut! "),
    };
}

help! How can I let them show the data that passed through the constructor?

1
It showed the original data in UserPostView.xaml, the NameView shows Name instead of Lily or Kevin Wang. - Kevin Wang

1 Answers

1
votes

If you could provide your ListView's xaml and DataTemplate we could provide you with a better answer.

However, my guess is your ListView's ItemsSource should be set to a List of ViewModel : INotifyPropertyChanged with your properties on it instead of on your ContentView and your ContentView should be bound to that ViewModel. For example:

public partial class UserPostView : ContentView
{
    public UserPostView()
    {
        InitializeComponent();
    }
}

//NOTE: in UserPostView.xaml 
<Image Source="{Binding Picture}" BackgroundColor="#ddd" />
<Label Text="{Binding PostOwner}" TextColor="#444" FontAttributes="Bold" Margin="10" />
<Label Text="{Binding PostOwnerLocation}" TextColor="#666" YAlign="Center" />
...

Example ViewModel:

public class UserPostViewModel : INotifyPropertyChanged
{
    ImageSource _picture;

    public ImageSource Picture
    {
       get { return _picture; }
       set 
       {
           _picture = value;
           OnPropertyChanged(nameof(Picture));
        }
    }

    string _postOwner;

    public string PostOwner
    {
        get { return _postOwner; }
        set 
        {
            _postOwner = value;
            OnPropertyChanged(nameof(PostOwner));
        }
    }

    string _postOwnerLocation;

    public string PostOwnerLocation
    {
        get { return _postOwnerLocation; }
        set 
        {
            _postOwnerLocation = value;
            OnPropertyChanged(nameof(PostOwnerLocation));
        }
    }

    ....

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

In your Page:

public TimelinePage()
{
    InitializeComponent();
    ImageSource pic = ImageSource.FromUri(new Uri("https://tse1.mm.bing.net/th?id=OIP.acchQ02P8HmrmwbjCTwG5AHaHa&pid=Api"));
    _listView.ItemsSource = new ObservableCollection<UserPostViewModel>
    {
        new UserPostViewModel { Picture = ImageSource.FromResource("Logo"), PostOwner="Kevin Wang", PostOwnerLocation="SHDR"},
        new UserPostViewModel { Picture = pic, PostOwner="Lily", PostOwnerLocation="SHDR"},
        new UserPostViewModel { Picture = pic, PostOwner="Zhang Shuo", PostOwnerLocation="FRDL"},
    };
}

//NOTE: In your Pages ListView DataTemplate
<ListView.ItemTemplate>
    <DataTemplate>
        <ViewCell>
            <local:UserPostView/>
        </ViewCell>
    </DataTemplate>
</ListView.ItemTemplate>