Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Use um ItemsRepeater para criar experiências de coleção personalizadas usando um sistema de layout flexível, exibições personalizadas e virtualização.
Ao contrário ListView, ItemsRepeater não fornece uma experiência abrangente ao usuário final – não tem interface do usuário padrão e não fornece nenhuma política em relação ao foco, seleção ou interação do usuário. Em vez disso, é um bloco de construção que você pode usar para criar suas próprias experiências exclusivas baseadas em coleção e controles personalizados. Embora não tenha uma política interna, ele permite que você anexe a política para criar a experiência necessária. Por exemplo, você pode definir o layout a ser usado, a política de teclado, a política de seleção, etc.
Você pode pensar em ItemsRepeater conceitualmente como um painel controlado por dados, em vez de como um controle completo como ListView. Você especifica uma coleção de itens de dados a serem exibidos, um modelo de item que gera um elemento de interface do usuário para cada item de dados e um layout que determina como os elementos são dimensionados e posicionados. Em seguida, o ItemsRepeater produz elementos filho com base na fonte de dados e exibe-os conforme especificado pelo template e layout dos itens. Os itens exibidos não precisam ser homogêneos porque ItemsRepeater pode carregar conteúdo para representar os itens de dados com base nos critérios especificados em um seletor de modelo de dados.
Será este o controlo correto?
Use um ItemsRepeater para criar exibições personalizadas para coleções de dados. Embora ele possa ser usado para apresentar um conjunto básico de itens, muitas vezes você pode usá-lo como o elemento de exibição no modelo de um controle personalizado.
Se você precisar de um controle pronto para uso para exibir dados em uma lista ou grade com personalização mínima, considere usar um ListView ou GridView.
ItemsRepeater não tem uma coleção Items interna. Se precisar fornecer diretamente uma coleção de Itens, em vez de vincular a uma fonte de dados separada, provavelmente necessitará de uma experiência de política de alto nível e deverá usar o ListView ou o GridView.
ItemsControl e ItemsRepeater permitem experiências de coleção personalizáveis, mas ItemsRepeater oferece suporte à virtualização de layouts de interface do usuário, enquanto ItemsControl não. Recomendamos usar ItemsRepeater em vez de ItemsControl, seja para apenas apresentar alguns itens de dados ou criar um controle de coleção personalizado.
Rolagem com ItemsRepeater
ItemsRepeater não deriva de Control, portanto, não tem um modelo de controle. Portanto, ele não contém nenhuma rolagem interna como um ListView ou outros controles de coleção.
Quando se usa um ItemsRepeater, deve-se fornecer a funcionalidade de rolagem colocando-o dentro de um controlo ScrollViewer.
Observação
Se o seu aplicativo for executado em versões anteriores do Windows - aquelas lançadas antes Windows 10, versão 1809 - então você também precisa hospedar o ScrollViewer dentro do ItemsRepeaterScrollHost.
<muxc:ItemsRepeaterScrollHost>
<ScrollViewer>
<muxc:ItemsRepeater ... />
</ScrollViewer>
</muxc:ItemsRepeaterScrollHost>
Se o seu aplicativo só será executado em versões recentes do Windows 10, versão 1809 e posterior - então não há necessidade de usar o ItemsRepeaterScrollHost.
Antes do Windows 10, versão 1809, o ScrollViewer não implementava a interface IScrollAnchorProvider que o ItemsRepeater precisava. O ItemsRepeaterScrollHost permite que o ItemsRepeater coordene com ScrollViewer em versões anteriores para preservar corretamente a localização visível dos itens que o usuário está visualizando. Caso contrário, os itens podem parecer se mover ou desaparecer repentinamente quando os itens na lista são alterados ou o aplicativo é redimensionado.
Criar um ItemsRepeater
- APIs importantes: classe ItemsRepeaterclasse ScrollViewer
O aplicativo WinUI 3 Gallery inclui exemplos interativos da maioria dos controles, recursos e funcionalidades do WinUI 3. Obtenha o aplicativo no da Microsoft Store ou obtenha o código-fonte em do GitHub
Para usar um ItemsRepeater, você precisa fornecer a ele os dados a serem exibidos definindo a propriedade ItemsSource. Em seguida, indique como exibir os itens definindo a propriedade ItemTemplate.
FonteDeItens
Para preencher o modo de exibição, defina a propriedade ItemsSource como uma coleção de itens de dados. Aqui, o ItemsSource é definido em código diretamente para uma instância de uma coleção.
ObservableCollection<string> Items = new ObservableCollection<string>();
ItemsRepeater itemsRepeater1 = new ItemsRepeater();
itemsRepeater1.ItemsSource = Items;
Você também pode vincular a propriedade ItemsSource a uma coleção em XAML. Para obter mais informações sobre vinculação de dados, consulte Visão geral da vinculação de dados.
<ItemsRepeater ItemsSource="{x:Bind Items}"/>
Modelo de Item
Para especificar como um item de dados é visualizado, defina a propriedade ItemTemplate como umDataTemplateou DataTemplateSelector que tenha definido. O modelo de dados define como os dados são visualizados. Por padrão, o item é exibido no modo de exibição com um TextBlock que usa a representação de cadeia de caracteres do objeto de dados.
No entanto, normalmente você deseja mostrar uma apresentação mais rica de seus dados usando um modelo que define o layout e a aparência de um ou mais controles que você usará para exibir um item individual. Os controles que você usa no modelo podem ser vinculados às propriedades do objeto de dados ou ter conteúdo estático definido embutido.
DataTemplate
Neste exemplo, o objeto de dados é uma cadeia de caracteres simples. O DataTemplate inclui uma imagem à esquerda do texto e estiliza o TextBlock para exibir a cadeia de caracteres em uma cor azul-escuro.
Observação
Quando utiliza a extensão de marcação x:Bind num DataTemplate, precisa especificar o DataType (x:DataType) no DataTemplate.
<DataTemplate x:DataType="x:String">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="47"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="Assets/placeholder.png" Width="32" Height="32"
HorizontalAlignment="Left"/>
<TextBlock Text="{x:Bind}" Foreground="Teal"
FontSize="15" Grid.Column="1"/>
</Grid>
</DataTemplate>
Veja como os itens apareceriam quando exibidos com este DataTemplate.
O número de elementos usados no DataTemplate para um item pode ter um impacto significativo no desempenho se a sua vista apresentar um grande número de itens. Para obter mais informações e exemplos de como usar DataTemplates para definir a aparência dos itens em sua lista, consulte Contêineres e modelos de item.
Sugestão
Para comodidade, quando pretender declarar o template inline em vez de referenciado como um recurso estático, pode especificar o DataTemplate ou DataTemplateSelector como o filho direto do ItemsRepeater. Será atribuído como valor da propriedade ItemTemplate. Por exemplo, isso é válido:
<ItemsRepeater ItemsSource="{x:Bind Items}">
<DataTemplate>
<!-- ... -->
</DataTemplate>
</ItemsRepeater>
Sugestão
Ao contrário do ListView e de outros controlos de coleção, o ItemsRepeater não envolve os elementos de um DataTemplate com um contêiner adicional que inclua definições padrão, como margens, preenchimento, visuais de seleção, ou estado visual ao passar o cursor. Em vez disso, ItemsRepeater apresenta apenas o que está definido no DataTemplate. Se quiser que seus itens tenham a mesma aparência de um item de exibição de lista, você pode incluir explicitamente um contêiner, como ListViewItem, em seu modelo de dados. ItemsRepeater mostrará os visuais do ListViewItem, mas não usa automaticamente outras funções, como seleção ou exibição da caixa de seleção múltipla.
Da mesma forma, se a sua coleção de dados for uma coleção de controlos reais, como Button (List<Button>), pode colocar um ContentPresenter no seu DataTemplate para exibir o controlo.
SeletorDeModeloDeDados
Os itens exibidos na exibição não precisam ser do mesmo tipo. Você pode fornecer a propriedade ItemTemplate com um DataTemplateSelector para selecionar diferentes DataTemplateem função de critérios que especificar.
Este exemplo pressupõe que um DataTemplateSelector foi definido que decide entre dois diferentes DataTemplates para representar um item Large e Small.
<ItemsRepeater ...>
<ItemsRepeater.ItemTemplate>
<local:VariableSizeTemplateSelector Large="{StaticResource LargeItemTemplate}"
Small="{StaticResource SmallItemTemplate}"/>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
Ao definir um DataTemplateSelector para usar com ItemsRepeater, você só precisa implementar uma sobrescrita do método SelectTemplateCore(Object). Para obter mais informações e exemplos, consulte DataTemplateSelector.
Observação
Uma alternativa ao DataTemplates para gerenciar como os elementos são criados em cenários mais avançados é implementar seu próprio IElementFactory para usar como o ItemTemplate. Será responsável pela geração de conteúdos quando solicitado.
Configurar a fonte de dados
Use a propriedade ItemsSource para especificar a coleção a ser usada para gerar o conteúdo dos itens. Você pode definir o ItemsSource para qualquer tipo que implemente IEnumerable. Interfaces de coleta adicionais implementadas pela fonte de dados determinam qual funcionalidade está disponível para o ItemsRepeater interagir com seus dados.
Esta lista mostra as interfaces disponíveis e quando considerar o uso de cada uma.
IEnumerable(.NET) / IIterable
Pode ser usado para pequenos conjuntos de dados estáticos.
No mínimo, a fonte de dados deve implementar a interface IEnumerable / IIterable. Se isso for tudo o que é suportado, o controle irá iterar tudo uma vez para criar uma cópia que ele pode usar para acessar itens por meio de um valor de índice.
IReadonlyList (.NET) / IVectorView
Pode ser usado para conjuntos de dados estáticos e somente leitura.
Permite que o controle acesse itens por índice e evita a cópia interna redundante.
-
Pode ser usado para conjuntos de dados estáticos.
Permite que o controle acesse itens por índice e evita a cópia interna redundante.
Aviso: Alterações na lista/vetor sem implementar INotifyCollectionChanged não serão refletidas na interface de utilizador.
INotifyCollectionChanged (.NET)
Recomendado para suportar a notificação de alterações.
Permite que o controle observe e reaja a alterações na fonte de dados e reflita essas alterações na interface do usuário.
-
Suporta notificação de alteração
Como a interface de INotifyCollectionChanged, isso permite ao controle observar e reagir às alterações na fonte de dados.
Aviso: O Windows.Foundation.IObservableVector<T> não suporta uma ação 'move'. Isso pode fazer com que a interface do usuário de um item perca seu estado visual. Por exemplo, um item que está atualmente selecionado e/ou tem foco onde o movimento é alcançado por um 'Remover' seguido de um 'Adicionar' perderá o foco e não será mais selecionado.
O Platform.Collections.Vetor<T> usa IObservableVector<T> e tem essa mesma limitação. Se for necessário suporte para uma ação 'Mover', use a interface INotifyCollectionChanged. A classe .NET ObservableCollection<T> usa INotifyCollectionChanged.
-
Quando um identificador exclusivo pode ser associado a cada item. Recomendado ao usar 'Redefinir' como a ação de alteração de coleção.
Permite que o controle recupere de forma muito eficiente a interface do utilizador existente depois de receber uma ação de 'Reset' como parte de um evento INotifyCollectionChanged ou IObservableVector. Depois de receber uma redefinição, o controle usará o ID exclusivo fornecido para associar os dados atuais aos elementos que ele já havia criado. Sem a chave para mapear o índice, o controle teria que assumir que deve recomeçar do princípio na criação da interface de utilizador para os dados.
As interfaces listadas acima, diferentes de IKeyIndexMapping, fornecem o mesmo comportamento em ItemsRepeater que em ListView e GridView.
As interfaces a seguir em um ItemsSource habilitam funcionalidade especial nos controles ListView e GridView, mas atualmente não têm efeito em um ItemsRepeater:
Sugestão
Queremos a sua opinião! Deixe-nos saber o que você pensa sobre o projeto WinUI GitHub. Considere adicionar seus pensamentos sobre propostas existentes, como #374: Adicione suporte de carregamento incremental para ItemsRepeater.
Uma abordagem alternativa para carregar incrementalmente seus dados à medida que o usuário rola para cima ou para baixo é observar a posição do visor do ScrollViewer e carregar mais dados à medida que o visor se aproxima da extensão.
<ScrollViewer ViewChanged="ScrollViewer_ViewChanged">
<ItemsRepeater ItemsSource="{x:Bind MyItemsSource}" .../>
</ScrollViewer>
private async void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (!e.IsIntermediate)
{
var scroller = (ScrollViewer)sender;
var distanceToEnd = scroller.ExtentHeight - (scroller.VerticalOffset + scroller.ViewportHeight);
// trigger if within 2 viewports of the end
if (distanceToEnd <= 2.0 * scroller.ViewportHeight
&& MyItemsSource.HasMore && !itemsSource.Busy)
{
// show an indeterminate progress UI
myLoadingIndicator.Visibility = Visibility.Visible;
await MyItemsSource.LoadMoreItemsAsync(/*DataFetchSize*/);
loadingIndicator.Visibility = Visibility.Collapsed;
}
}
}
Alterar o layout dos itens
Os itens mostrados pelo ItemsRepeater são organizados por um objeto Layout que gerencia o dimensionamento e o posicionamento de seus elementos filho. Quando usado com um ItemsRepeater, o objeto Layout permite a virtualização da interface do usuário. Os layouts fornecidos são StackLayout e UniformGridLayout. Por padrão, ItemsRepeater usa um StackLayout com orientação vertical.
StackLayout
StackLayout organiza elementos em uma única linha que você pode orientar horizontal ou verticalmente.
Você pode definir a propriedade Espaçamento para ajustar a quantidade de espaço entre os itens. O espaçamento é aplicado na direção do de Orientaçãodo layout.
Este exemplo mostra como definir a propriedade ItemsRepeater.Layout como um StackLayout com orientação horizontal e espaçamento de 8 pixels.
<!-- xmlns:muxc="using:Microsoft.UI.Xaml.Controls" -->
<muxc:ItemsRepeater ItemsSource="{x:Bind Items}" ItemTemplate="{StaticResource MyTemplate}">
<muxc:ItemsRepeater.Layout>
<muxc:StackLayout Orientation="Horizontal" Spacing="8"/>
</muxc:ItemsRepeater.Layout>
</muxc:ItemsRepeater>
UniformGridLayout
O UniformGridLayout posiciona elementos sequencialmente em um layout de encapsulamento. Os itens são dispostos em ordem da esquerda para a direita quando o de Orientação é Horizontale dispostos de cima para baixo quando a Orientação é Vertical. Cada item é dimensionado igualmente.
O número de itens em cada linha de um layout horizontal é influenciado pela largura mínima do item. O número de itens em cada coluna de um layout vertical é influenciado pela altura mínima do item.
- Pode fornecer explicitamente um tamanho mínimo para usar, definindo as propriedades MinItemHeight e MinItemWidth.
- Se você não especificar um tamanho mínimo, o tamanho medido do primeiro item será considerado o tamanho mínimo por item.
Você também pode definir o espaçamento mínimo entre linhas e colunas no layout, configurando as propriedades MinColumnSpacing e MinRowSpacing.
Depois que o número de itens em uma linha ou coluna tiver sido determinado com base no tamanho e espaçamento mínimos do item, pode haver espaço não utilizado após o último item na linha ou coluna (como ilustrado na imagem anterior). Você pode especificar se algum espaço extra será ignorado, usado para aumentar o tamanho de cada item ou usado para criar espaço extra entre os itens. Isso é controlado pelas propriedades ItemsStretch e ItemsJustification.
Você pode definir a propriedade ItemsStretch para especificar como o tamanho do item é aumentado para preencher o espaço não utilizado.
Esta lista mostra os valores disponíveis. As definições assumem a Orientação padrão de Horizontal.
- Nenhum: O espaço extra não é utilizado no final da linha. Este é o padrão.
- de preenchimento: Os itens recebem largura extra para usar o espaço disponível (altura se vertical).
- Uniforme: Os itens recebem largura extra para usar o espaço disponível e ganham altura extra para manter a proporção (a altura e a largura são invertidas se os itens estiverem verticais).
Esta imagem mostra o efeito dos valores de ItemsStretch em um layout horizontal.
Quando ItemsStretch estiver None, você poderá definir a propriedade ItemsJustification para especificar como o espaço extra é usado para alinhar os itens.
Esta lista mostra os valores disponíveis. As definições assumem a Orientação padrão de Horizontal.
- Início: Os itens estão alinhados ao início da linha. O espaço extra fica sem uso no final da linha. Este é o padrão.
- Centro: Os itens são alinhados no centro da fila. O espaço extra é dividido uniformemente no início e no final da linha.
- Fim: Os itens estão alinhados com o final da linha. O espaço extra é deixado não utilizado no início da linha.
- SpaceAround: Os itens são distribuídos uniformemente. Uma quantidade igual de espaço é adicionada antes e depois de cada item.
- SpaceBetween: Os itens são distribuídos uniformemente. Uma quantidade igual de espaço é adicionada entre cada item. Nenhum espaço é adicionado no início e no final da linha.
- SpaceEvenly: Os itens são distribuídos uniformemente com uma quantidade igual de espaço entre cada item e no início e no final da linha.
Esta imagem mostra o efeito dos valores de ItemsStretch em um layout vertical (aplicado a colunas em vez de linhas).
Sugestão
A propriedade ItemsStretch afeta a passagem de medida do layout. A propriedade ItemsJustification afeta a organizar passagem de layout.
Este exemplo mostra como definir a propriedade ItemsRepeater.Layout como um UniformGridLayout.
<!-- xmlns:muxc="using:Microsoft.UI.Xaml.Controls" -->
<muxc:ItemsRepeater ItemsSource="{x:Bind Items}"
ItemTemplate="{StaticResource MyTemplate}">
<muxc:ItemsRepeater.Layout>
<muxc:UniformGridLayout MinItemWidth="200"
MinColumnSpacing="28"
ItemsJustification="SpaceAround"/>
</muxc:ItemsRepeater.Layout>
</muxc:ItemsRepeater>
Eventos do ciclo de vida
Quando você hospeda itens em um ItemsRepeater, talvez seja necessário executar alguma ação quando um item é mostrado ou deixa de ser mostrado, como iniciar um download assíncrono de algum conteúdo, associar o elemento a um mecanismo para controlar a seleção ou interromper alguma tarefa em segundo plano.
Em um controle de virtualização, você não pode confiar em eventos Loaded/Unloaded porque o elemento pode não ser removido da árvore visual ao vivo quando é reciclado. Em vez disso, outros eventos são fornecidos para gerenciar o ciclo de vida dos elementos. Este diagrama mostra o ciclo de vida de um elemento em um ItemsRepeater e quando os eventos relacionados são gerados.
- ElementPrepared ocorre sempre que um elemento é preparado para uso. Isso ocorre tanto para um elemento recém-criado quanto para um elemento que já existe e está sendo reutilizado da fila de reciclagem.
- ElementClearing ocorre imediatamente cada vez que um elemento é enviado para a fila de reciclagem, tal como quando o elemento está fora do intervalo de itens realizados.
- ElementIndexChanged ocorre para cada UIElement realizado em que o índice do item que ele representa foi alterado. Por exemplo, quando outro item é adicionado ou removido na fonte de dados, o índice para itens que vêm depois na ordem recebe esse evento.
Este exemplo mostra como você pode usar esses eventos para anexar um serviço de seleção personalizado para controlar a seleção de itens em um controle personalizado que usa ItemsRepeater para exibir itens.
<!-- xmlns:muxc="using:Microsoft.UI.Xaml.Controls" -->
<UserControl ...>
...
<ScrollViewer>
<muxc:ItemsRepeater ItemsSource="{x:Bind Items}"
ItemTemplate="{StaticResource MyTemplate}"
ElementPrepared="OnElementPrepared"
ElementIndexChanged="OnElementIndexChanged"
ElementClearing="OnElementClearing">
</muxc:ItemsRepeater>
</ScrollViewer>
...
</UserControl>
interface ISelectable
{
int SelectionIndex { get; set; }
void UnregisterSelectionModel(SelectionModel selectionModel);
void RegisterSelectionModel(SelectionModel selectionModel);
}
private void OnElementPrepared(ItemsRepeater sender, ElementPreparedEventArgs args)
{
var selectable = args.Element as ISelectable;
if (selectable != null)
{
// Wire up this item to recognize a 'select' and listen for programmatic
// changes to the selection model to know when to update its visual state.
selectable.SelectionIndex = args.Index;
selectable.RegisterSelectionModel(this.SelectionModel);
}
}
private void OnElementIndexChanged(ItemsRepeater sender, ElementIndexChangedEventArgs args)
{
var selectable = args.Element as ISelectable;
if (selectable != null)
{
// Sync the ID we use to notify the selection model when the item
// we represent has changed location in the data source.
selectable.SelectionIndex = args.NewIndex;
}
}
private void OnElementClearing(ItemsRepeater sender, ElementClearingEventArgs args)
{
var selectable = args.Element as ISelectable;
if (selectable != null)
{
// Disconnect handlers to recognize a 'select' and stop
// listening for programmatic changes to the selection model.
selectable.UnregisterSelectionModel(this.SelectionModel);
selectable.SelectionIndex = -1;
}
}
Classificar, filtrar e redefinir os dados
Quando você executa ações como filtrar ou classificar seu conjunto de dados, tradicionalmente pode ter comparado o conjunto de dados anterior com os novos dados e, em seguida, emitido notificações granulares de alteração por meio INotifyCollectionChanged . No entanto, muitas vezes é mais fácil substituir completamente os dados antigos pelos novos dados e desencadear uma notificação de alteração na coleção usando a ação Redefinir.
Normalmente, uma redefinição faz com que um controle libere elementos filho existentes e comece de novo, criando a interface do usuário desde o início na posição de rolagem 0, pois não tem consciência de exatamente como os dados foram alterados durante a redefinição.
No entanto, se a coleção atribuída como ItemsSource oferecer suporte a identificadores exclusivos implementando a interface IKeyIndexMapping, o ItemsRepeater poderá identificar rapidamente:
- UIElements reutilizáveis para dados que existiam antes e depois da redefinição
- itens visíveis anteriormente que foram removidos
- novos itens adicionados que serão visíveis
Isso permite que o ItemsRepeater evite recomeçar a partir da posição de rolagem 0. Ele também permite restaurar rapidamente os UIElements para dados que não foram alterados em uma redefinição, resultando em melhor desempenho.
Este exemplo mostra como exibir uma lista de itens em uma pilha vertical onde MyItemsSource é uma fonte de dados personalizada que encapsula uma lista subjacente de itens. Ele expõe uma propriedade Data que pode ser usada para reatribuir uma nova lista a ser usada como fonte de itens, o que, em seguida, desencadeia uma redefinição.
<ScrollViewer x:Name="sv">
<ItemsRepeater x:Name="repeater"
ItemsSource="{x:Bind MyItemsSource}"
ItemTemplate="{StaticResource MyTemplate}">
<ItemsRepeater.Layout>
<StackLayout ItemSpacing="8"/>
</ItemsRepeater.Layout>
</ItemsRepeater>
</ScrollViewer>
public MainPage()
{
this.InitializeComponent();
// Similar to an ItemsControl, a developer sets the ItemsRepeater's ItemsSource.
// Here we provide our custom source that supports unique IDs which enables
// ItemsRepeater to be smart about handling resets from the data.
// Unique IDs also make it easy to do things apply sorting/filtering
// without impacting any state (i.e. selection).
MyItemsSource myItemsSource = new MyItemsSource(data);
repeater.ItemsSource = myItemsSource;
// ...
// We can sort/filter the data using whatever mechanism makes the
// most sense (LINQ, database query, etc.) and then reassign
// it, which in our implementation triggers a reset.
myItemsSource.Data = someNewData;
}
// ...
public class MyItemsSource : IReadOnlyList<ItemBase>, IKeyIndexMapping, INotifyCollectionChanged
{
private IList<ItemBase> _data;
public MyItemsSource(IEnumerable<ItemBase> data)
{
if (data == null) throw new ArgumentNullException();
this._data = data.ToList();
}
public IList<ItemBase> Data
{
get { return _data; }
set
{
_data = value;
// Instead of tossing out existing elements and re-creating them,
// ItemsRepeater will reuse the existing elements and match them up
// with the data again.
this.CollectionChanged?.Invoke(
this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
#region IReadOnlyList<T>
public ItemBase this[int index] => this.Data != null
? this.Data[index]
: throw new IndexOutOfRangeException();
public int Count => this.Data != null ? this.Data.Count : 0;
public IEnumerator<ItemBase> GetEnumerator() => this.Data.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
#endregion
#region INotifyCollectionChanged
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
#region IKeyIndexMapping
private int lastRequestedIndex = IndexNotFound;
private const int IndexNotFound = -1;
// When UniqueIDs are supported, the ItemsRepeater caches the unique ID for each item
// with the matching UIElement that represents the item. When a reset occurs the
// ItemsRepeater pairs up the already generated UIElements with items in the data
// source.
// ItemsRepeater uses IndexForUniqueId after a reset to probe the data and identify
// the new index of an item to use as the anchor. If that item no
// longer exists in the data source it may try using another cached unique ID until
// either a match is found or it determines that all the previously visible items
// no longer exist.
public int IndexForUniqueId(string uniqueId)
{
// We'll try to increase our odds of finding a match sooner by starting from the
// position that we know was last requested and search forward.
var start = lastRequestedIndex;
for (int i = start; i < this.Count; i++)
{
if (this[i].PrimaryKey.Equals(uniqueId))
return i;
}
// Then try searching backward.
start = Math.Min(this.Count - 1, lastRequestedIndex);
for (int i = start; i >= 0; i--)
{
if (this[i].PrimaryKey.Equals(uniqueId))
return i;
}
return IndexNotFound;
}
public string UniqueIdForIndex(int index)
{
var key = this[index].PrimaryKey;
lastRequestedIndex = index;
return key;
}
#endregion
}
Criar um controle de coleção personalizado
Você pode usar o ItemsRepeater para criar um controle de coleção personalizado completo com seu próprio tipo de controle para apresentar cada item.
Observação
Isso é semelhante ao uso de ItemsControl, mas em vez de derivar de ItemsControl e colocar um ItemsPresenter no modelo de controle, você deriva de Control e insere um ItemsRepeater no modelo de controle. O controlo de coleção personalizado "tem um" ItemsRepeater em vez de "é um" ItemsControl. Isso implica que você também deve escolher explicitamente quais propriedades expor, em vez de quais propriedades herdadas não suportar.
Este exemplo mostra como colocar um ItemsRepeater no modelo de um controle personalizado chamado MediaCollectionView e expor suas propriedades.
<!-- xmlns:muxc="using:Microsoft.UI.Xaml.Controls" -->
<Style TargetType="local:MediaCollectionView">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MediaCollectionView">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="ScrollViewer">
<muxc:ItemsRepeater x:Name="ItemsRepeater"
ItemsSource="{TemplateBinding ItemsSource}"
ItemTemplate="{TemplateBinding ItemTemplate}"
Layout="{TemplateBinding Layout}"
TabFocusNavigation="{TemplateBinding TabFocusNavigation}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public sealed class MediaCollectionView : Control
{
public object ItemsSource
{
get { return (object)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemsSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(MediaCollectionView), new PropertyMetadata(0));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemTemplate. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register(nameof(ItemTemplate), typeof(DataTemplate), typeof(MediaCollectionView), new PropertyMetadata(0));
public Layout Layout
{
get { return (Layout)GetValue(LayoutProperty); }
set { SetValue(LayoutProperty, value); }
}
// Using a DependencyProperty as the backing store for Layout. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LayoutProperty =
DependencyProperty.Register(nameof(Layout), typeof(Layout), typeof(MediaCollectionView), new PropertyMetadata(0));
public MediaCollectionView()
{
this.DefaultStyleKey = typeof(MediaCollectionView);
}
}
Exibir itens agrupados
Você pode aninhar um ItemsRepeater no ItemTemplate de outro ItemsRepeater para criar layouts de virtualização aninhados. A estrutura fará uso eficiente dos recursos, minimizando a carga desnecessária de elementos que não são visíveis ou que não estão próximos à janela de visualização atual.
Este exemplo mostra como você pode exibir uma lista de itens agrupados em uma pilha vertical. O ItemsRepeater externo gera cada grupo. No modelo para cada grupo, outro ItemsRepeater gera os itens.
<!-- xmlns:muxc="using:Microsoft.UI.Xaml.Controls" -->
<Page.Resources>
<muxc:StackLayout x:Key="MyGroupLayout"/>
<muxc:StackLayout x:Key="MyItemLayout" Orientation="Horizontal"/>
</Page.Resources>
<ScrollViewer>
<muxc:ItemsRepeater ItemsSource="{x:Bind AppNotifications}"
Layout="{StaticResource MyGroupLayout}">
<muxc:ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="ExampleApp:AppNotifications">
<!-- Group -->
<StackPanel>
<!-- Header -->
<TextBlock Text="{x:Bind AppTitle}"/>
<!-- Items -->
<muxc:ItemsRepeater ItemsSource="{x:Bind Notifications}"
Layout="{StaticResource MyItemLayout}"
ItemTemplate="{StaticResource MyTemplate}"/>
<!-- Footer -->
<Button Content="{x:Bind FooterText}"/>
</StackPanel>
</DataTemplate>
</muxc:ItemsRepeater.ItemTemplate>
</muxc:ItemsRepeater>
</ScrollViewer>
A imagem abaixo mostra o layout básico criado usando o exemplo acima como diretriz.
Este próximo exemplo mostra um layout para um aplicativo que tem várias categorias que podem ser alteradas com a preferência do usuário e são apresentadas como listas de rolagem horizontal. O layout deste exemplo também é representado pela imagem acima.
<!-- xmlns:muxc="using:Microsoft.UI.Xaml.Controls" -->
<!-- Include the <muxc:ItemsRepeaterScrollHost> if targeting Windows 10 versions earlier than 1809. -->
<ScrollViewer>
<muxc:ItemsRepeater ItemsSource="{x:Bind Categories}"
Background="LightGreen">
<muxc:ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="local:Category">
<StackPanel Margin="12,0">
<TextBlock Text="{x:Bind Name}" Style="{ThemeResource TitleTextBlockStyle}"/>
<!-- Include the <muxc:ItemsRepeaterScrollHost> if targeting Windows 10 versions earlier than 1809. -->
<ScrollViewer HorizontalScrollMode="Enabled"
VerticalScrollMode="Disabled"
HorizontalScrollBarVisibility="Auto" >
<muxc:ItemsRepeater ItemsSource="{x:Bind Items}"
Background="Orange">
<muxc:ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="local:CategoryItem">
<Grid Margin="10"
Height="60" Width="120"
Background="LightBlue">
<TextBlock Text="{x:Bind Name}"
Style="{StaticResource SubtitleTextBlockStyle}"
Margin="4"/>
</Grid>
</DataTemplate>
</muxc:ItemsRepeater.ItemTemplate>
<muxc:ItemsRepeater.Layout>
<muxc:StackLayout Orientation="Horizontal"/>
</muxc:ItemsRepeater.Layout>
</muxc:ItemsRepeater>
</ScrollViewer>
</StackPanel>
</DataTemplate>
</muxc:ItemsRepeater.ItemTemplate>
</muxc:ItemsRepeater>
</ScrollViewer>
Trazendo um elemento para a exibição
A estrutura XAML já lida com a exibição de um FrameworkElement quando ele 1) recebe o foco do teclado ou 2) recebe o foco do Narrador. Pode haver outros casos em que você precise explicitamente trazer um elemento à vista. Por exemplo, em resposta a uma ação do usuário ou para restaurar o estado da interface do usuário após uma navegação de página.
Trazer um elemento virtualizado à vista envolve o seguinte:
- Implementar um UIElement para um item
- Execute o layout para garantir que o elemento tenha uma posição válida
- Iniciar uma solicitação para exibir o elemento realizado
O exemplo abaixo demonstra essas etapas como parte da restauração da posição de rolagem de um item em uma lista plana e vertical após uma navegação de página. No caso de dados hierárquicos que utilizem ItemsRepeaters aninhados, a abordagem é essencialmente a mesma, mas precisa ser aplicada em cada nível da hierarquia.
<ScrollViewer x:Name="scrollviewer">
<ItemsRepeater x:Name="repeater" .../>
</ScrollViewer>
public class MyPage : Page
{
// ...
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// retrieve saved offset + index(es) of the tracked element and then bring it into view.
// ...
var element = repeater.GetOrCreateElement(index);
// ensure the item is given a valid position
element.UpdateLayout();
element.StartBringIntoView(new BringIntoViewOptions()
{
VerticalOffset = relativeVerticalOffset
});
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
base.OnNavigatingFrom(e);
// retrieve and save the relative offset and index(es) of the scrollviewer's current anchor element ...
var anchor = this.scrollviewer.CurrentAnchor;
var index = this.repeater.GetElementIndex(anchor);
var anchorBounds = anchor.TransformToVisual(this.scrollviewer).TransformBounds(new Rect(0, 0, anchor.ActualSize.X, anchor.ActualSize.Y));
relativeVerticalOffset = this.scrollviewer.VerticalOffset - anchorBounds.Top;
}
}
Ativar acessibilidade
ItemsRepeater não fornece uma experiência de acessibilidade padrão. A documentação sobre Usabilidade para aplicativos do Windows fornece uma grande variedade de informações para ajudá-lo a garantir que seu aplicativo forneça uma experiência de usuário inclusiva. Se estiveres a usar o ItemsRepeater para criar um controlo personalizado, certifica-te de ver a documentação sobre pares de automação personalizados.
Teclado
O suporte mínimo de teclado para movimento de foco que ItemsRepeater fornece é baseado no Navegação Direcional 2D para Tecladodo XAML.
O modo XYFocusKeyboardNavigation do ItemsRepeater está habilitado por padrão. Dependendo da experiência pretendida, considere adicionar suporte para interações comuns de teclado , tais como Home, End, PageUp, e PageDown.
O ItemsRepeater garante automaticamente que a ordem de tabulação padrão para seus itens (virtualizados ou não) siga a mesma ordem em que os itens são fornecidos nos dados. Por padrão, o ItemsRepeater tem sua propriedade TabFocusNavigation definida como Once em vez do padrão comum de Local.
Observação
O ItemsRepeater não se lembra automaticamente do último item focado. Isso significa que quando um usuário está usando Shift+Tab ele pode ser levado para o último item realizado.
Anunciar "Item X de Y" em leitores de ecrã
Você precisa gerenciar a configuração das propriedades de automação apropriadas, como valores para PositionInSet e SizeOfSet, e garantir que elas permaneçam up-todata quando os itens são adicionados, movidos, removidos, etc.
Em alguns layouts personalizados, pode não haver uma sequência óbvia para a ordem visual. Os utilizadores esperam pelo menos que os valores das propriedades PositionInSet e SizeOfSet, usadas pelos leitores de ecrã, correspondam à ordem em que os itens aparecem nos dados (com um deslocamento de 1 para coincidir com a contagem natural versus ser baseado em 0).
A melhor maneira de conseguir isso é fazer com que o par de automação do controlo de itens implemente os métodos GetPositionInSetCore e GetSizeOfSetCore e relate a posição do item no conjunto de dados representado pelo controlo. O valor só é calculado em tempo de execução quando acessado por uma tecnologia assistiva e mantê-lo up-to-date torna-se um não-problema. O valor corresponde à ordem dos dados.
Este exemplo mostra como você pode fazer isso ao apresentar um controle personalizado chamado CardControl.
<ScrollViewer >
<ItemsRepeater x:Name="repeater" ItemsSource="{x:Bind MyItemsSource}">
<ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="local:CardViewModel">
<local:CardControl Item="{x:Bind}"/>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</ScrollViewer>
internal sealed class CardControl : CardControlBase
{
protected override AutomationPeer OnCreateAutomationPeer() => new CardControlAutomationPeer(this);
private sealed class CardControlAutomationPeer : FrameworkElementAutomationPeer
{
private readonly CardControl owner;
public CardControlAutomationPeer(CardControl owner) : base(owner) => this.owner = owner;
protected override int GetPositionInSetCore()
=> ((ItemsRepeater)owner.Parent)?.GetElementIndex(this.owner) + 1 ?? base.GetPositionInSetCore();
protected override int GetSizeOfSetCore()
=> ((ItemsRepeater)owner.Parent)?.ItemsSourceView?.Count ?? base.GetSizeOfSetCore();
}
}
UWP e WinUI 2
Importante
As informações e exemplos neste artigo são otimizados para aplicativos que usam o SDK de Aplicativos Windows e WinUI 3, mas geralmente são aplicáveis a aplicativos UWP que usam WinUI 2. Consulte a referência da API UWP para obter informações e exemplos específicos da plataforma.
Esta seção contém informações que você precisa para usar o controle em um aplicativo UWP ou WinUI 2.
O ItemsRepeater para aplicativos UWP requer WinUI 2. Para obter mais informações, incluindo instruções de instalação, consulte WinUI 2. As APIs para esse controle existem no namespace Microsoft.UI.Xaml.Controls.
- APIs UWP:classe ScrollViewer
- WinUI 2 Apis:classe ItemsRepeater
- Abra o aplicativo WinUI 2 Gallery e veja o ItemsRepeater em ação. O aplicativo WinUI 2 Gallery inclui exemplos interativos da maioria dos controles, recursos e funcionalidades do WinUI 2. Obtenha o aplicativo da Microsoft Store ou obtenha o código-fonte no GitHub.
Para usar o código neste artigo com WinUI 2, use um alias em XAML (usamos muxc) para representar as APIs da Biblioteca da Interface do Usuário do Windows incluídas em seu projeto. Consulte Introdução ao WinUI 2 para obter mais informações.
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
<muxc:ItemsRepeater />
Artigos relacionados
Windows developer