Xamarin形式:RepeaterView不会绑定动态数据,但可以使用listView

时间:2018-12-12 07:45:14

标签: listview xamarin.forms repeater

我正在尝试使用服务调用返回的结果填充列表视图。

PFB我的XAML代码:

    <StackLayout Orientation="Vertical" BackgroundColor="#FFFFFF">
        <!-- Doesn't Work -->
        <repeater:RepeaterView ItemsSource="{Binding DashboardItems}">
                    <repeater:RepeaterView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <StackLayout>
                                    <Label Text="{Binding Title}"></Label>
                                </StackLayout>
                            </ViewCell>
                        </DataTemplate>
                    </repeater:RepeaterView.ItemTemplate>
            </repeater:RepeaterView>

        <!-- Works -->
        <ListView ItemsSource="{Binding DashboardItems}">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <StackLayout>
                                    <Label Text="{Binding Title}"></Label>
                                </StackLayout>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
            </ListView>
            </StackLayout>

ViewModel:

public class DashboardSummaryViewModel : BaseViewModel, System.ComponentModel.INotifyPropertyChanged
{

    public ObservableCollection<DashboardItem> DashboardItems { get; set; } = new ObservableCollection<DashboardItem>();

    public override void Init()
    {
        base.Init();


        GetSummary();
    }

    private void GetSummary(){

        // If I update DashboardItems value here, it even works with repeaterView control 
        var paywithSdk = DependencyService.Get<IPaywithSDK>();
        if (paywithSdk != null)
        {
            paywithSdk.SetListener(new PaywithSDKListener
            {
                OnSuccess = user =>
                {
                    if (user != null)
                    {
                        user.DateUpdated = DateTime.Now;
                        LoadSummaryDetails(user);
                    }

                },
                OnError = error =>
                {
                    var user = UserDetails.CreateDefault();
                    user.ResponseString = DataContractSerialiser.JsonSerialize(error);
                    //RefreshUserDetails(user);
                }
            });

        }
    }

    private void LoadSummaryDetails(UserDetails user)
    {
        DashboardItem dashboardItem = new DashboardItem();
        dashboardItem.Tile = new DashboardTile();
        dashboardItem.Tile.Title = "Wallet";
        dashboardItem.Tile.ItemDetailsPage = AppPage.PROFILE_DETAILS;

        dashboardItem.SubTitle = new DashboadTileContent();
        dashboardItem.SubTitle.IsVisible = false;

        dashboardItem.ItemDetailsPageMessage = new DashboadTileContent();
        dashboardItem.ItemDetailsPageMessage.Message = "Click through to view your transactions";
        dashboardItem.ItemDetailsPageMessage.IsVisible = true;

        dashboardItem.StatusType = new DashboadTileContent();
        dashboardItem.StatusType.Message = "Current balance";
        dashboardItem.StatusType.IsVisible = true;

        dashboardItem.StatusValue = new DashboadTileContent();
        dashboardItem.StatusValue.Message = user.TotalBalance.ToString();
        dashboardItem.StatusValue.IsVisible = true;

        dashboardItem.SummaryItems = new DashboardTileSummaryItems();
        dashboardItem.SummaryItems.Items = new List<DashboardTileSummaryItem>();

        foreach(var item in WalletSummaryDetails.UserSalPacks){
            DashboardTileSummaryItem dashboardTileSummaryItem = new DashboardTileSummaryItem();
            dashboardTileSummaryItem.DisplayName = item.DisplayName;
            dashboardTileSummaryItem.Value = Convert.ToString(item.Balance);
            dashboardItem.SummaryItems.Items.Add(dashboardTileSummaryItem);
        }
        dashboardItem.TileSubLinks = new DashboardTileSubLinks();
        dashboardItem.TileSubLinks.Links = new List<DashboardTileSubLink>(){
            new DashboardTileSubLink(){DisplayName = "View Offers", LinkDetailsPage = AppPage.PROFILE_DETAILS},
            new DashboardTileSubLink(){DisplayName = "Report Lost/Stolen", LinkDetailsPage = AppPage.PROFILE_DETAILS}
        };

        dashboardItem.TileSubLinks.IsVisible = true;

        WallterDashboardItem = dashboardItem;

        DashboardItems.Add(dashboardItem);

    }
}

DashboardItems是试图绑定到XAML中的集合成员属性。

此属性值在“ paywithSdk.SetListener”调用的OnSuccess回调函数中更新。使用更新的值,数据不会绑定到RepeaterView控件。转发器视图没有显示绑定到该视图的值。

但是,如果我将此集合属性绑定到ListView控件,则它可以正常工作,并且它将获得更新的值,如上面的XAML代码一样。

我的问题是,我想使用自定义的stackLayout控件自定义listItem元素,我想为其使用RepeaterView而不是ListView控件。

即使在同一线程调用中而不是在回调方法调用中更新了集合值,即使使用repeaterView,该值也将被绑定。但是我需要将RepeaterView控件与“ paywithSdk.SetListener” OnSuccess回调方法中更新的数据绑定。

请让我知道如何使用RepeaterView控件绑定更新的数据。

PFB中继器查看代码:

namespace XamarinForms.Plugin.Repeater
{
public class RepeaterView : StackLayout
{
    public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(
        nameof(ItemTemplate),
        typeof(DataTemplate),
        typeof(RepeaterView),
        default(DataTemplate));

    public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(
        nameof(ItemsSource),
        typeof(ICollection),
        typeof(RepeaterView),
        null,
        BindingMode.OneWay,
        propertyChanged: ItemsChanged);

    public RepeaterView()
    {
        Spacing = 0;
    }

    public ICollection ItemsSource
    {
        get => (ICollection)GetValue(ItemsSourceProperty);
        set => SetValue(ItemsSourceProperty, value);
    }

    public DataTemplate ItemTemplate
    {
        get => (DataTemplate)GetValue(ItemTemplateProperty);
        set => SetValue(ItemTemplateProperty, value);
    }

    protected virtual View ViewFor(object item)
    {
        View view = null;

        if (ItemTemplate != null)
        {
            var content = ItemTemplate.CreateContent();

            view = content is View ? content as View : ((ViewCell)content).View;

            view.BindingContext = item;
        }

        return view;
    }

    private static void ItemsChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var control = bindable as RepeaterView;

        if (control == null) return;

        control.Children.Clear();

        var items = (ICollection)newValue;

        if (items == null) return;

        foreach (var item in items)
        {
            control.Children.Add(control.ViewFor(item));
        }
    }
}

}

1 个答案:

答案 0 :(得分:0)

问题在于,对ObservableCollection进行动态更改(例如,添加/删除项目)不会为RepeaterView类中的ItemSourceProperty触发PropertyChangedEvent,因为它仍然是同一对象。分配新实例将触发事件,依此类推,请调用ItemsChanged方法。

一种解决方案是订阅ObservableCollection的CollectionChanged事件(see reference),如果该项目的项目发生更改(例如,添加/删除或清除整个集合),则将触发该事件,然后调用ItemsChanged方法更新视图

RepeaterView实现可与ObservableCollection配合使用,并在更改后完美地更新视图。

希望我的回答仍然有帮助!