我在Xamarin + mvvmcross中找到了几种(不太多)不同的方法来分组细胞。我已经尝试了几个,但是我遇到了一个问题:当我的真实服务返回更新的集合(稍有延迟)时,绑定并没有真正起作用,列表仍然是空的。如果要运行虚假服务,它会立即返回结果,列表中会填充数据。
我尝试了几种方法,但没有人推动我前进,所以我不确定是否需要任何代码。只是询问是否有关于分组的现代实现的样本/提示。
编辑:这是一个代码示例。目前的版本基于Stuart的答案:Unable to bind to ios table section cells in mvvmcross, child cells bind as expected
ViewModel:
public override async void OnShow()
{
var calendarList = await DataService.GetListAsync();
CalendarList = new List<Model>(calendarList.OrderBy(a => a.Date));
}
因此,viewmodel获取模型列表,按日期排序并将其设置为CalendarList。 CalendarList只是一个抛出通知的List(新的就是这个工作)。
查看,初始化:
public override void ViewDidLoad()
{
base.ViewDidLoad();
var source = new TableSource(CalendarList);
this.AddBindings(new Dictionary<object, string>
{
{source, "ItemsSource CalendarList" }
});
CalendarList.Source = source;
CalendarList.ReloadData();
}
View,TableSource
public class TableSource : MvxSimpleTableViewSource
{
private static readonly NSString CalendarCellIdentifier = new NSString("CalendarCell");
private List<IGrouping<DateTime, Model>> GroupedCalendarList;
public TableSource(UITableView calendarView) : base(calendarView, "CalendarCell", "CalendarCell")
{
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
var foo = GroupedCalendarList[indexPath.Section].ToList();
var item = foo[indexPath.Row];
var cell = GetOrCreateCellFor(tableView, indexPath, item);
var bindable = cell as IMvxDataConsumer;
if (bindable != null)
bindable.DataContext = item;
return cell;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
var numberRows = GroupedCalendarList[(int)section].Count();
return numberRows;
}
public override UIView GetViewForHeader(UITableView tableView, nint section)
{
var label = new UILabel();
label.Text = GroupedCalendarList[(int)section].FirstOrDefault().ScheduledStart.Value.Date.ToString();
label.TextAlignment = UITextAlignment.Center;
return label;
}
public override nint NumberOfSections(UITableView tableView)
{
return GroupedCalendarList.Count;
}
public override void ReloadTableData()
{
if (ItemsSource == null) return;
var calendarList = new List<Model>(ItemsSource as List<ActivityModel>);
List<IGrouping<DateTime, Model>> groupedCalendarList = calendarList.GroupBy(cl => cl.ScheduledStart.Value.Date).ToList();
if (ItemsSource != null)
GroupedCalendarList = new List<IGrouping<DateTime, Model>>(groupedCalendarList);
}
}
其中一个症状是当VM更新列表时不会调用ReloadTableData()。
编辑2:好吧,我已经尝试使用ObservableCollection,我看到调用了ReloadTableData,但是UI仍然是空的。如果有人能提供工作样本 - 那就太好了。
更新了VM:
public override async void OnShow()
{
var calendarList = await DataService.GetListAsync();
CalendarList.Clear();
foreach (var item in calendarList.OrderBy(a => a.ScheduledStart.Value.Date))
{
CalendarList.Add(item); // Lets bruteforce and send notification on each add. In real life it's better to use extension ObservableCollection.AddRange().
}
}
更新了视图:
public override void ReloadTableData()
{
if (ItemsSource == null) return;
var calendarList = ItemsSource as IEnumerable<Model>;
var groupedCalendarList = calendarList.GroupBy(cl => cl.ScheduledStart.Value.Date).ToList();
GroupedCalendarList = new List<IGrouping<DateTime, Model>>(groupedCalendarList);
Mvx.Trace("Trying to add new item " + calendarList.Count());
}
在这种情况下,输出已满
诊断:9.54尝试添加新项目77
但是UI列表仍然是空的。
EDIT3:如果用添加Task.Run()替换VM.OnShow()。Wait();到VM的ctor(所以它会延迟View.ViewDidLoad()直到加载数据) - 然后列表显示正确。
public ViewModel()
{
Task.Run(async () =>
{
var calendarList = await DataService.GetListAsync();
CalendarList = new ObservableCollection<ActivityModel>(calendarList);
}).Wait(); // This guy is called before View.ViewDidLoad() and grouped list is shown properly
}
答案 0 :(得分:3)
这里可能存在许多问题。
var set = this.CreateBindingSet<TClass, TViewModel>();
set.Bind(source).To(vm => vm.Collection);
set.Apply();
使用可观察的集合,然后在完成对可观察集合调用的添加后:
RaisePropertyChanged(() => Collection);
为了便于对数据进行分组,我重写了ItemsSource,将数据推送到值的字典和键数组中,以便更容易计算出各个部分。
由于您只想提供文本,只需覆盖TitleForHeader:
public override string TitleForHeader(UITableView tableView, nint section)
答案 1 :(得分:1)
如果你不共享一些代码,很难给出好的建议。但是,通过阅读您的问题,我认为您使用的是List<T>
而不是ObservableCollection<T>
。 UI需要知道集合何时更新,因此需要INotifyCollectionChanged
和INotifyPropertyChanged
实现。 ObservableCollection
实现了这些接口。
<强>视图模型:强>
确保CalendarList
的类型为ObservableCollection<Model>
。在此更改后,您将不需要方法ReloadTable()
。在课程MvxSimpleTableViewSource
中,您可以在MvxTableViewSource
ReloadTableData
中调用他的基础setter
ItemsSource
,您可以在以下链接中看到:
public override async void OnShow()
{
var calendarList = await DataService.GetListAsync();
CalendarList = new ObservableCollection<Model>(calendarList.OrderBy(a => a.Date));
}
答案 2 :(得分:1)
我是瞎子!感谢来自MvvmCross Slack channel的@nmilcoff突出显示问题。我忘了打电话给base.ReloadTableData()。
当然,感谢皮拉图斯和斯普林厄姆提供了很好的提示。
以下是解决方案:
public override void ReloadTableData()
{
if (ItemsSource == null) return;
var calendarList = new List<Model>(ItemsSource as List<ActivityModel>);
List<IGrouping<DateTime, Model>> groupedCalendarList = calendarList.GroupBy(cl => cl.ScheduledStart.Value.Date).ToList();
if (ItemsSource != null)
GroupedCalendarList = new List<IGrouping<DateTime, Model>>(groupedCalendarList);
base.ReloadTableData(); // Here we are
}
答案 3 :(得分:0)
似乎您在发布数据请求后立即更新数据列表。
这是不正确的,这是异步操作,您应该在http请求完成时更新数据。
有一个完成异步数据请求的示例:
string strURL = "https://api.bitcoinaverage.com/ticker/";
MyHTTPRequestManager.Instance.GetDataFromUrl (strURL,(string dataStr)=>{
Console.WriteLine("Getting data succeed");
Console.WriteLine("The dataStr = "+dataStr);
//update your dataList here
InvokeOnMainThread(delegate {
//Update your tableView or collectionView here, all UI stuff must be invoke on Main thread
});
});
这是MyHTTPRequestManager.cs:
public class MyHTTPRequestManager
{
public delegate void GettingDataCallback(string dataStr);
private static MyHTTPRequestManager instance = null;
public static MyHTTPRequestManager Instance{
get{
if(null == instance)
instance = new MyHTTPRequestManager();
return instance;
}
}
public void GetDataFromUrl(string strURL,GettingDataCallback callback)
{
Console.WriteLine ("Begin request data.");
System.Net.HttpWebRequest request;
request = (System.Net.HttpWebRequest)WebRequest.Create(strURL);
System.Net.HttpWebResponse response;
response = (System.Net.HttpWebResponse)request.GetResponse();
System.IO.StreamReader myreader = new System.IO.StreamReader(response.GetResponseStream(), Encoding.UTF8);
string responseText = myreader.ReadToEnd();
myreader.Close();
Console.WriteLine ("Getting succeed, invoke callback.");
callback.Invoke (responseText);
}
}
这是结果屏幕截图:
希望它可以帮到你。