Привязка общего объекта (список <T>) к пользовательскому элементу управления Xamarin - PullRequest
0 голосов
/ 30 мая 2019

Я занимаюсь разработкой мобильного приложения, в котором есть 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
        };
    }
}

Я создал элемент управления, который может выбирать постраничные результаты, элемент управления отображает список элементов текущей страницы и позволяет пользователю перемещаться по другим страницам (на рисунке ниже).

The pagination control I built

Я создал доказательство концепции этого элемента управления, однако наткнулся на небольшой, но действительно раздражающий камень преткновения.

Мой PagedResult<T> является общим, но PagedItemSource для моего списка не является. Я создал элемент управления, чтобы иметь привязываемые свойства: DataTemplate, PagedItemSource & LoadPageCommand. Сам элемент управления определяет, какая страница будет загружена, и сам выполнит эту команду (я могу выставить это значение позже, но пока это не нужно).

При создании элемента управления мне нужно было инициализировать PagedResult<T>, однако вместо того, чтобы беспокоиться о дженериках, я просто превратил его в PagedResult<object> и сделал то же самое в моей модели представления. Теперь я нахожусь в точке, где я хочу, чтобы этот список был универсальным, отчасти по той причине, что я построил этот элемент управления, чтобы он был универсальным и абстрактным, как большая ответственность за подкачку к элементу управления. Однако из-за того, что моя модель API использует универсальный, я застрял!

Я не могу инициализировать свой контроль с помощью Generic 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>, однако это бесполезно, потому что теперь мне нужно отслеживать тип моих данных в моей модели представления ...

Я придумал хакерскую работу, чтобы я мог взломать приложение, но оно глупое и кажется грязным, и я его ненавижу ...

Мой хак - иметь второе свойство в моей модели представления, которое просто берет текущий PagedResult<T> и преобразует его в PagedResult<object>, и связывает это свойство с элементом управления. (Извините, что вам пришлось это прочитать)

Я пошел и получил трал для исходного кода Xamarin.Forms, чтобы посмотреть, как они сделали это для ListView, поскольку это по сути та же проблема, и их ListView принимает любой универсальный объект. К сожалению, я обнаружил, что src невероятно запутан, и не узнал ничего, что помогло бы решить мою проблему (я видел в ItemView<Cell>, что ItemSource - это IEnumerable, но я не мог понять, как они устанавливали T этого ...

Любая помощь будет высоко ценится, я, вероятно, смотрю на это неправильно!

1 Ответ

2 голосов
/ 31 мая 2019

Как уже упоминал Эд Планкетт, вы думаете о неправильном пути.

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)
);

И из этого привязываемого свойства вы можете получить доступ к списку данных, а также к информации о подкачке.

Надеюсь, это поможет.

...