【问题标题】:How to Bind data to a Custom ListView with Xamarin Android with Reactive UI如何使用 Xamarin Android 和 Reactive UI 将数据绑定到自定义 ListView
【发布时间】:2017-11-28 06:50:16
【问题描述】:

我正在使用带有响应式 UI 的 Xamarin Android,而不是使用 Xamarin Forms。我有一个自定义 ListView(我已将其布局定义为 xaml)。我不知道如何使用活动类中的 OneWayBind 方法将此控件绑定到 ViewModel 中的 observableCollection。

我把它写成,

this.OneWayBind(ViewModel, x => x.OutletListing, x => x.List).DisposeWith(SubscriptionDisposables);

但是给,

System.ArgumentException:无法转换 System.Collections.ObjectModel.ObservableCollection1 到 Android.Widget.ListView。要解决此问题,请注册一个 IBindingTypeConverter

我在教程中看到 Xamarin 表单为此使用了 ItemSource 属性。

任何人都可以为此提供解决方案。 提前致谢。

更新 我不知道如何继续给出给定的答案。我想了解更多。

这是我的 ViewModel 类。

using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Diagnostics;
    using Android.App;
    using Android.Content;
    using Android.OS;
    using Android.Runtime;
    using Android.Views;
    using Android.Widget;
    using ReactiveUI;
    using System.Collections.ObjectModel;
    using System.Reactive.Linq;
    using Splat;
    using System.Reactive.Disposables;
    using System.Threading.Tasks;

    namespace DistributrIII.Mobile.Droid.ViewModels
    {
        public class StockTakeVM : ReactiveObject
        {
            protected Lazy<CompositeDisposable> ViewModelBindings = new Lazy<CompositeDisposable>(() => new CompositeDisposable());
            public void RegisterObservables()
            {
                StockItemListing = new ReactiveList<StockItemListingResult>();

                this.LoadStockItems = ReactiveCommand.CreateFromTask<FilterParams, List<StockItemListingResult>>(
                    async filter =>
                    {
                        System.Diagnostics.Debug.WriteLine("Load StockItemListing #1");
                        var r = await GetStockItemListing(filter);
                        return r;
                    },
                        Observable.Return(true))
                    .DisposeWith(ViewModelBindings.Value);

                this.LoadStockItems.ThrownExceptions
                    .Subscribe(ex =>
                    {
                        System.Diagnostics.Debug.WriteLine("Load StockItemListing Failed");
                    });

                this.LoadStockItems
                   .ObserveOn(RxApp.MainThreadScheduler)
                   .Subscribe(result =>
                   {

                       StockItemListing.Clear();
                       foreach (var item in result)
                           StockItemListing.Add(item);

                   });

            }

            async Task<List<StockItemListingResult>> GetStockItemListing(FilterParams filter)
            {
                List<StockItemListingResult> items = new List<StockItemListingResult>() {  

                new StockItemListingResult
                {
                    StockItemName = "PORK SAUSAGES (ECONOMY) 500G",
                    StockItemCode = "CODE: J3103386",
                    StockItemAmt = "150"
                },

                new StockItemListingResult
                {
                    StockItemName = "COLLAR BACON  500G",
                    StockItemCode = "CODE: J3155667",
                    StockItemAmt = "152"
                },

                new StockItemListingResult
                {
                    StockItemName = "COLLAR BACON 1KG",
                    StockItemCode = "CODE: J2344545",
                    StockItemAmt = "200"
                },

                new StockItemListingResult
                {
                    StockItemName = "PORK CHIPPOLATAS 1KG",
                    StockItemCode = "CODE: J31038779",
                    StockItemAmt = "378"
                },

                new StockItemListingResult
                {
                    StockItemName = "PORK SAUSAGES (ECONOMY) 500G",
                    StockItemCode = "CODE: J3103386",
                    StockItemAmt = "23"
                },
                new StockItemListingResult
                {
                    StockItemName = "PORK SAUSAGES (ECONOMY) 500G",
                    StockItemCode = "CODE: J3103386",
                    StockItemAmt = "454"
                },

                new StockItemListingResult
                {
                    StockItemName = "COLLAR BACON  500G",
                    StockItemCode = "CODE: J3155667",
                    StockItemAmt = "123"
                },

                new StockItemListingResult
                {
                    StockItemName = "COLLAR BACON 1KG",
                    StockItemCode = "CODE: J2344545",
                    StockItemAmt = "675"
                },

                new StockItemListingResult
                {
                    StockItemName = "PORK CHIPPOLATAS 1KG",
                    StockItemCode = "CODE: J31038779",
                    StockItemAmt = "11"
                },

                new StockItemListingResult
                {
                    StockItemName = "PORK SAUSAGES (ECONOMY) 500G",
                    StockItemCode = "CODE: J3103386",
                    StockItemAmt = "34"
                }


            };
                return items;
            }


            // Observable Properties
            ReactiveList<StockItemListingResult> _stockItemListing;
            public ReactiveList<StockItemListingResult> StockItemListing
            {
                get { return _stockItemListing; }
                private set { this.RaiseAndSetIfChanged(ref _stockItemListing, value); }
            }

            ReactiveCommand<FilterParams, List<StockItemListingResult>> _loadStockItems;
            public ReactiveCommand<FilterParams, List<StockItemListingResult>> LoadStockItems
            {
                get { return _loadStockItems; }
                private set { this.RaiseAndSetIfChanged(ref _loadStockItems, value); }
            }
            public StockTakeVM()
            {
                RegisterObservables();
            }

        }

        public class StockItemListingResult : ReactiveObject
        {
            Guid _stockItemId;
            public Guid Id
            {
                get { return _stockItemId; }
                set { this.RaiseAndSetIfChanged(ref _stockItemId, value); }
            }

            string _stockItemName;
            public string StockItemName
            {
                get { return _stockItemName; }
                set { this.RaiseAndSetIfChanged(ref _stockItemName, value); }
            }

            string _stockItemCode;
            public string StockItemCode
            {
                get { return _stockItemCode; }
                set { this.RaiseAndSetIfChanged(ref _stockItemCode, value); }
            }
            string _stockItemAmt;
            public string StockItemAmt
            {
                get { return _stockItemAmt; }
                set { this.RaiseAndSetIfChanged(ref _stockItemAmt, value); }
            }

        }


    }

我的活动类如下

   using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using ReactiveUI;
using Android.Support.V4.Widget;
using Android.Support.Design.Widget;
using DistributrIII.Mobile.Droid.ViewModels;
using DistributrIII.Mobile.Droid.Util;
using System.Reactive.Disposables;
using DistributrIII.Mobile.Droid.Models;
using Android.Support.V7.Widget;
using Android.Support.V7.App;
using Splat;
using Android.Support.V4.View;
using System.Reactive.Linq;

namespace DistributrIII.Mobile.Droid.Views.StockTake
{
    [Activity(Label = "StockTakeActivity", MainLauncher = true, Theme = "@style/MainTheme")]
    public class StockTakeActivity : DistributrBaseActivity<StockTakeVM>
    {
        private Android.Support.V7.Widget.SearchView _searchView;
        public ListView List { get; private set; }
        ReactiveList<StockItemListingResult> StockListItems;
        StockTakeScreenAdapter osadapter;
        List<StockItemModel> items = new List<StockItemModel>();

        public StockTakeActivity()
        {
            this.ViewModel = new StockTakeVM();
            this.StockListItems = new ReactiveList<StockItemListingResult>();
        }

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            SetContentView(Resource.Layout.Activity_StockTake);


            this.Bind(ViewModel, x => x.StockItemListing, x => x.StockListItems);

            //checking whether change happens
            //        StockListItems.ItemChanged
            //        .Where(x => x.PropertyName == "StockItemAmt" && x.Sender.StockItemAmt)
            //        .Select(x => x.Sender)
            //        .Subscribe(x => {
            //            Console.WriteLine("Make sure to save {0}!", x.DocumentName);
            //});
            this.WhenAnyValue(view => view.ViewModel.StockItemListing)
            .Where(listing => listing != null)
            .Select(listing => new DistributrIII.Mobile.Droid.Util.ReactiveListAdapter<StockTakeVM>(listing, Resource.Layout.Activity_StockTake))
            .BindTo(this, view => view.List.Adapter);

            this.ViewModel.LoadStockItems.Execute(new FilterParams
            {
                NameFilter = "",

                Status = "All"
            }).Subscribe();

            //List = FindViewById<ListView>(Resource.Id.List);

            SetupReactiveLists(this);
            var toolbarST = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbarST);
            toolbarST.InflateMenu(Resource.Menu.StockTakeSearch);
            toolbarST.Title = "Stock Take";




        }
        public void SetupReactiveLists(Activity context)
        {
            List = FindViewById<ListView>(Resource.Id.List);

            foreach (var item in StockListItems)
            {
                StockItemModel stockitem = new StockItemModel
                {
                    StockItemName = item.StockItemName,
                    StockItemCode = item.StockItemCode
                };

                items.Add(stockitem);
            }
            osadapter = new StockTakeScreenAdapter(this, items);
            List.Adapter = osadapter;

            List.ItemClick += OnListItemClick;

        }




}

这是我的 Listadapter 类。

public class StockTakeScreenAdapter : BaseAdapter<StockItemModel>
    {
        List<StockItemModel> items;
        Activity context;
        public StockTakeScreenAdapter(Activity context, List<StockItemModel> items) : base()
        {
            this.context = context;
            this.items = items;
        }
        public override long GetItemId(int position)
        {
            return position;
        }
        public override StockItemModel this[int position]
        {
            get { return items[position]; }
        }
        public override int Count
        {
            get { return items.Count; }
        }
        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            var item = items[position];
            View view = convertView;
            if (view == null) 
                view = context.LayoutInflater.Inflate(Resource.Layout.ViewCell_StockTake, null);
            view.FindViewById<TextView>(Resource.Id.stockitem_name).Text = item.StockItemName;
            view.FindViewById<TextView>(Resource.Id.stockitem_code).Text = item.StockItemCode;
            view.FindViewById<TextView>(Resource.Id.stockitem_amt).Text = item.StockItemAmt;
            return view;

} }

这是我的问题

  1. 我是否应该在 Activity 类中创建另一个响应式列表以进行绑定。
  2. 如果我更改了列表项的值,它是否会更新列表(我希望使用另一个片段类来更新列表项的值。)

我是使用 RX 进行 android 开发的新手。如果我把事情搞砸了,请原谅我。再次感谢。

【问题讨论】:

    标签: c# listview data-binding xamarin.android reactiveui


    【解决方案1】:

    您可以在 Android 上使用适配器来完成此操作,就像没有响应式扩展一样。

    如果你想在 RxUI 中使用 ListView,你可以使用ReactiveListAdapter。不幸的是,这还没有真正记录在案,所以你可能想看看源代码:https://github.com/reactiveui/ReactiveUI/blob/develop/src/ReactiveUI/Platforms/android/ReactiveListAdapter.cs

    要点是你创建一个实例,ReactiveList 提供数据。然后,适配器会监视此列表中的更改以了解何时需要更新。

    例子:

    this.WhenAnyValue (view => view.ViewModel.OutletListing)
        .Where (listing => listing != null)
        .Select (listing => new ReactiveListAdapter (listing, MyViewCreator))
        .BindTo (this, view => view.List.Adapter);
    

    MyViewCreator 是一个委托,它接受列表中的初始 ViewModel 和父 ViewGroup,因此您可以使用初始数据填充行并返回结果视图。

    【讨论】:

    • 谢谢!我会尝试一下然后返回
    • 嗨,你好,我不知道如何回答你的问题。我已经更新了问题。你能帮帮我吗。谢谢
    • 回答您的问题: 1. 不,只需在您的 ViewModel 中创建正确的列表,这就是它的用途。 2. 仅当您在ReactiveList 上启用ChangeTrackingEnabled。我不确定您的 DistributrIII.Mobile.Droid.Util.ReactiveListAdapter 包含什么。
    猜你喜欢
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-14
    • 1970-01-01
    • 2021-03-03
    相关资源
    最近更新 更多