我正在开发一个移动应用程序,该应用程序具有可以调用以获取数据的API,大多数端点都已分页并返回以下对象:
public class PagedResult<T>
{
public List<T> Data { get; private set; }
public PagingInfo Paging { get; private set; }
public PagedResult(IEnumerable<T> items, int pageNo, int pageSize, long totalRecordCount)
{
Data = new List<T>(items);
Paging = new PagingInfo
{
PageNo = pageNo,
PageSize = pageSize,
TotalRecordCount = totalRecordCount,
PageCount = totalRecordCount > 0
? (int)Math.Ceiling(totalRecordCount / (double)pageSize)
: 0
};
}
}
我建立了一个可以选择分页结果的控件,该控件显示当前页面项目的列表,并允许用户在其他页面之间移动(如下图所示)。
我已经为此控件建立了概念证明,但是我遇到了一个很小但很烦人的绊脚石。
我的PagedResult<T>
是通用的,但我的列表视图的PagedItemSource
不是通用的。我将控件构建为具有可绑定属性:DataTemplate
,PagedItemSource
和LoadPageCommand
。控件本身确定要加载的页面并自行执行该命令(我可能稍后会公开此值,但现在不需要)。
在构建控件时,我必须初始化PagedResult<T>
,但是不必担心泛型,我只是将其放入PagedResult<object>
,并在视图模型中进行了相同的操作。现在,我要使该列表通用,这是我构建此控件的部分原因是通用和抽象,以及对控件分页的责任。但是由于我的API模型使用泛型,因此我被卡住了!
我无法使用通用T初始化控件:
public partial class PaginationControl<T>: ContentView
{
...
}
这将意味着C#编译器开始对我大喊大叫,整个类实际上都发疯了。我的绑定属性显示警告:Static field in generic type
。这就是为什么我只使用<object>
作为列表类型的原因,不幸的是,在以下情况下绑定将不起作用:
查看模型
public PagedResult<MyObject> PagedItemSource { get; set; }
控件
public static readonly BindableProperty PagedItemSourceProperty = BindableProperty.Create(
nameof(PagedItemSource),
typeof(PagedResult<object>),
typeof(PaginationControl),
defaultValue: null,
propertyChanged: (bindable, oldVal, newVal) => ((PaginationControl)bindable).OnPagedItemSourceChanged((PagedResult<object>)newVal)
);
如果我的视图模型的数据也为<object>
类型,它将起作用,但这没用,因为现在我需要在视图模型中跟踪数据的类型... < / p>
我想出了一个解决办法,以便可以破解该应用程序,但是它很愚蠢,感觉很脏,我讨厌它...
我的技巧是在视图模型中拥有第二个属性,该属性仅采用当前的PagedResult<T>
并将其转换为PagedResult<object>
,并将此属性绑定到控件。 (对不起,您必须阅读此书)
我去了Xamarin.Forms源代码拖网,以查看他们如何为ListView
完成此操作,因为这本质上是相同的问题,并且它们的ListView
接受任何通用对象。不幸的是,我发现src令人迷惑不解,没有学到任何东西可以帮助解决我的问题(我在ItemView<Cell>
中看到ItemSource
是IEnumerable
,但我不知道他们是如何设置{{ 1}} ...
任何帮助将不胜感激,我可能是用错误的方式看待这个问题!
答案 0 :(得分:2)
正如Ed Plunkett所说,您正在考虑错误的方式。
ItemsSource
的类型为IEnumerable
,那里没有通用参数,因为ListView
并不关心项目的特定类型。您的控件也应该是这种情况,否则您将无法使其通用。
因此,为什么不遵循它们为大多数集合实现的相同设计,例如List<T>
也实现IList<T>
和IList
作为接口。当您对通用参数不感兴趣而只想访问列表时,可以使用最后一个接口。完成此设置后,我们可以进行以下操作:
public interface IPagedResult
{
IEnumerable Data { get; }
PagingInfo Paging { get; }
//any other properties you might need
}
然后我们可以这样声明PagedResult
:
public class PagedResult<T> : IPagedResult
{
public List<T> List { get; private set; }
public IEnumerable Data => List;
public PagingInfo Paging { get; private set; }
public PagedResult(IEnumerable<T> items, int pageNo, int pageSize, long totalRecordCount)
{
List = new List<T>(items);
Paging = new PagingInfo
{
PageNo = pageNo,
PageSize = pageSize,
TotalRecordCount = totalRecordCount,
PageCount = totalRecordCount > 0
? (int)Math.Ceiling(totalRecordCount / (double)pageSize)
: 0
};
}
}
现在您应该可以像这样声明BindableProperty
:
public static readonly BindableProperty PagedItemSourceProperty = BindableProperty.Create(
nameof(PagedItemSource),
typeof(IPagedResult),
typeof(PaginationControl),
defaultValue: null,
propertyChanged: (bindable, oldVal, newVal) => ((PaginationControl)bindable).OnPagedItemSourceChanged((IPagedResult)newVal)
);
从此可绑定属性中,您可以访问数据列表以及分页信息。
希望这会有所帮助。