Atualizar a interface do usuário quando Collections sofrer alteração
Nesta lição, o usuário selecionará cores favoritas. As cores disponíveis são listadas em uma lista suspensa (um controle ComboBox
). O usuário escolhe uma cor e a adiciona aos favoritos pressionando um botão. As cores favoritas serão exibidas abaixo. A escolha de uma cor favorita também exibirá um botão que permitirá ao usuário remover a cor escolhida de seus favoritos.
Primeiro, você adicionará o recurso de escolher uma cor da coleção LotsOfColors
e a adicionará à lista de cores favoritas.
1. Criar a propriedade SelectedColor
Vamos começar com o código e depois trataremos da interface do usuário.
Precisamos de uma forma de determinar qual item (ou seja, instância da classe ColorDescriptor
) o usuário escolheu no menu suspenso. O controle ComboBox
tem uma propriedade SelectedItem
, que obtém e define o item escolhido no momento. Assim, poderemos associar essa propriedade a uma propriedade do tipo ColorDescriptor
em nosso código.
Abra o ColorListLogic.cs e adicione o seguinte código:
private ColorDescriptor _selectedColor;
public ColorDescriptor SelectedColor
{
get => _selectedColor;
set => Set(ref _selectedColor, value);
}
Você já deve conhecer bem esse padrão. Ele representa uma propriedade padrão decorada com o mecanismo INotifyPropertyChanged
com a ajuda da classe base ObservableObject
.
2. Criar a lista FavoriteColors
A lista FavoriteColors
armazenará as cores que o usuário escolheu como favoritas. É apenas uma propriedade simples.
public List<ColorDescriptor> FavoriteColors { get; } =
new List<ColorDescriptor>();
3. Adicionar a cor escolhida aos Favoritos
A adição da cor escolhida aos Favoritos ocorrerá no método AddSelectedColorToFavorites
.
public void AddSelectedColorToFavorites()
{
FavoriteColors.Add(SelectedColor);
}
Pressupõe-se que quando esse método é chamado, SelectedColor
é preenchido com a cor que deve ser adicionada à lista de favoritos.
Com isso, concluímos o código (por enquanto). Vamos voltar nossa atenção ao XAML.
4. Alterar a ListBox
para uma ComboBox
Como queremos que a lista completa de cores seja mostrada em um menu suspenso (que é um controle ComboBox
), precisamos alterar o XAML. Felizmente, ListBox
e ComboBox
descendem do controle ItemsControl
e funcionam de forma semelhante, apesar da diferença na aparência e no comportamento. Tudo o que precisamos fazer é substituir ListBox
por ComboBox
no arquivo ColorList.xaml. Você pode usar o comando Editar>Encontrar e Substituir>Substituição rápida (Ctrl+H) para realizar a substituição.
Se você executar rapidamente o aplicativo agora, verá que ListBox
foi substituído por ComboBox
, mas as cores ainda são exibidas usando o mesmo modelo.
5. Extrair o modelo para um recurso
Em relação ao modelo, você precisará reutilizá-lo na lista de cores favoritas posteriormente. É uma boa ideia armazenar o modelo em um só lugar, para que o XAML seja mais legível. Mais importante, isso garante que as alterações no modelo afetem todas as instâncias. Vamos extrair o modelo para um recurso.
Qualquer FrameworkElement
pode ter sua lista de recursos. Optamos por tornar nosso modelo global para esta página, portanto, vamos adicionar uma marca <Page.Reources>
acima do elemento <Grid>
. Em seguida, mova toda a marca <DataTemplate>
e o conteúdo dentro dela.
<Page.Resources>
<DataTemplate x:DataType="local:ColorDescriptor">
<StackPanel Orientation="Horizontal">
<Rectangle Width="30"
Height="30">
<Rectangle.Fill>
<SolidColorBrush Color="{x:Bind Color}"/>
</Rectangle.Fill>
</Rectangle>
<TextBlock Text="{x:Bind Name}"
Margin="20, 10, 0, 10"/>
</StackPanel>
</DataTemplate>
</Page.Resources>
O Visual Studio avisa-o que os objetos dentro da marca <Page.Resources>
(que é um IDictionary
) devem ter um atributo Key
, então adicione-o ao <DataTemplate>
.
<DataTemplate x:Key="ColorTemplate"
x:DataType="local:ColorDescriptor">
Essa chave permite que façamos referência a este modelo de outro lugar na página, como o ComboBox.ItemTemplate
, que perdeu seu conteúdo. Para que a ComboBox
use o recurso ColorDescriptor
, você pode remover a marca <ComboBox.ItemTemplate>
e usá-la como um atributo dentro da própria marca <ComboBox>
. Toda a marca <ComboBox>
tem a seguinte aparência:
<ComboBox ItemsSource="{x:Bind Logic.LotsOfColors}"
Margin="20"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ColorTemplate}"/>
Você pode executar o aplicativo novamente apenas para verificar se o modelo de item funciona.
6. Compilar o restante da interface do usuário
A interface do usuário é simples. Todos os controles (o menu suspenso, o botão Adicionar a Favoritos, a lista de favoritos (com o texto do cabeçalho) e o botão Remover dos Favoritos) são colocados em um único StackPanel
vertical. Substitua todo o conteúdo do elemento <Grid>
pelo seguinte:
<StackPanel>
<ComboBox ItemsSource="{x:Bind Logic.LotsOfColors}"
Margin="20, 20, 20, 0"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ColorTemplate}"
SelectedItem="{x:Bind Logic.SelectedColor, Mode=TwoWay}"
/>
<Button Margin="20"
Click="{x:Bind Logic.AddSelectedColorToFavorites}">Add to Favorites</Button>
<TextBlock FontSize="25"
Margin="20, 20, 20, 0">Favorite colors</TextBlock>
<ListBox ItemsSource="{x:Bind Logic.FavoriteColors}"
ItemTemplate="{StaticResource ColorTemplate}"
Margin="20, 20, 20, 0"/>
<Button Margin="20">Remove from Favorites</Button>
</StackPanel>
Há uma associação TwoWay
entre o item atualmente selecionado na ComboBox
e a propriedade SelectedColor
da classe ColorListLogic
.
7. Execute o aplicativo
Execute o aplicativo agora, escolha uma cor em ComboBox
e selecione o botão Adicionar aos favoritos. Nada acontecerá. Adicionar um ponto de interrupção no final do método AddSelectedColorToFavorites
na classe ColorListLogic
mostra que o código funciona. A cor selecionada é adicionada à lista FavoriteColors
.
A interface do usuário não reflete as alterações nesta List<ColorDescriptor>
porque precisa ser notificada quando a coleção é alterada. No caso das listas, isso é feito pela interface System.Collections.Specialized.INotifyCollectionChanged
. Felizmente, isso não precisa ser implementado. A classe System.Collections.ObjectModel.ObservableCollection<T>
já possui tudo aquilo de que precisamos.
Para fazer nosso aplicativo funcionar, tudo que precisamos fazer é usar a classe ObservableCollection<T>
em vez da classe List<T>
na propriedade FavoriteColors
.
public ObservableCollection<ColorDescriptor> FavoriteColors { get; } =
new ObservableCollection<ColorDescriptor>();
Se você executar o aplicativo agora, a escolha das cores no menu suspenso e o botão Adicionar a Favoritos já funcionarão corretamente. As cores favoritas escolhidas serão refletidas na ListBox
. Ótimo!
8. Evite adicionar itens vazios
Quando você inicia o aplicativo, não há cores marcadas na lista suspensa. Se você marcar o botão Adicionar a Favoritos agora, null
s serão adicionados à lista. É um bug e vamos corrigi-lo!
Poderíamos adicionar uma verificação de null
ao método AddSelectedColorToFavorites
, mas isso não impediria que o botão Adicionar a Favoritos aparecesse quando não fosse funcional. Em vez disso, vamos garantir que sempre haja um item escolhido no menu suspenso. Como a propriedade SelectedItem
do menu suspenso é associada de duas maneiras à propriedade SelectedColor
no código, vamos apenas inicializá-la com um valor válido no início. Adicione a linha a seguir no final do construtor ColorListLogic
:
SelectedColor = LotsOfColors[0];
Isso garante que o primeiro item da lista LotsOfColors
seja selecionado quando o aplicativo for iniciado. O usuário não será capaz de adicionar um null
à coleção FavoriteColors
.
9. Remover cores favoritas
A próxima etapa é adicionar a capacidade de remover cores favoritas da ListBox
. Isso acontecerá quando o usuário escolher um item na ListBox
e selecionar o botão Remover dos Favoritos.
Com um procedimento similar ao aplicado à ComboBox
, podemos rastrear o item que o usuário escolheu na ListBox
usando a propriedade SelectedItem
. Podemos associar isso a uma propriedade no código. Adicione esse código à classe ColorListLogic
.
private ColorDescriptor _selectedFavoriteColor;
public ColorDescriptor SelectedFavoriteColor
{
get => _selectedFavoriteColor;
set
{
Set(ref _selectedFavoriteColor, value);
RaisePropertyChanged(nameof(IsRemoveFavoriteColorButtonVisible));
}
}
public bool IsRemoveFavoriteColorButtonVisible => SelectedFavoriteColor != null;
O código anterior também inclui uma propriedade booliana para controlar se o botão para remover itens da lista de favoritos deve ficar visível. Qualquer alteração realizada em SelectedFavoriteColor
fará com que a interface do usuário consulte essa propriedade e aja em conformidade.
Para realmente executar a remoção da cor da lista de Favoritos, precisamos gravar mais um método.
public void RemoveFavoriteColor()
{
FavoriteColors.Remove(SelectedFavoriteColor);
}
Para conectar o botão no XAML, abra ColorList.xaml e altere o XAML do botão Remover dos Favoritos. Faça isso para que ele inclua a associação Visibility
, bem como a associação Click
.
<Button Margin="20"
Visibility="{x:Bind Logic.IsRemoveFavoriteColorButtonVisible, Mode=OneWay}"
Click="{x:Bind Logic.RemoveFavoriteColor}">Remove from Favorites</Button>
Falta apenas associar o SelectedItem
do ListBox
à propriedade Logic.SelectedFavoriteColor
. Adicione o atributo SelectedItem
ao ListBox
no XAML.
<ListBox SelectedItem="{x:Bind Logic.SelectedFavoriteColor, Mode=TwoWay}"... >
Execute o aplicativo agora e verifique se consegue adicionar cores à lista de cores favoritas e removê-las. Observe como o botão Remover dos Favoritos aparece e desaparece de acordo com a escolha de uma cor favorita que pode ser removida.
Resumo
Esta lição ilustrou como você pode adquirir e configurar o item escolhido em um controle ComboBox
ou ListBox
ao associar suas propriedades SelectedItem
a uma propriedade C#. Você também viu que o uso de ObservableCollection
no código faz com que a interface do usuário atualize automaticamente o conteúdo dos controles que exibem vários itens.
Nesta lição, o usuário selecionará as cores favoritas dele. As cores disponíveis são listadas em uma lista suspensa (um controle ComboBox
). O usuário escolhe uma cor e a adiciona aos favoritos pressionando um botão. As cores favoritas são exibidas abaixo da lista completa. A escolha de uma cor favorita também exibirá um botão que permitirá ao usuário remover a cor escolhida de seus favoritos.
Primeiro, vamos adicionar a capacidade de escolher uma cor da coleção LotsOfColors
e adicioná-la à lista de cores favoritas.
1. Criar a propriedade SelectedColor
Vamos começar com o código e depois trataremos das alterações na interface do usuário.
Precisamos de uma forma de determinar qual item (ou seja, instância da classe ColorDescriptor
) o usuário escolheu no menu suspenso. O controle ComboBox
tem uma propriedade SelectedItem
, que obtém e define o item escolhido no momento. Assim, poderemos associar essa propriedade a uma propriedade do tipo ColorDescriptor
em nosso código.
Abra ColorListDataContext.cs
e adicione o seguinte código:
private ColorDescriptor? _selectedColor;
public ColorDescriptor? SelectedColor
{
get => _selectedColor;
set => Set(ref _selectedColor, value);
}
Você já deve conhecer bem esse padrão. Ele representa uma propriedade padrão que aproveita o mecanismo INotifyPropertyChanged
com a ajuda da classe base ObservableObject
.
2. Criar a lista FavoriteColors
A lista FavoriteColors
armazenará as cores que o usuário escolheu como favoritas. É apenas uma propriedade simples.
public List<ColorDescriptor> FavoriteColors { get; } =
new List<ColorDescriptor>();
3. Adicionar a cor escolhida aos Favoritos
A adição da cor escolhida aos Favoritos ocorrerá no método AddSelectedColorToFavorites
. Por precaução, confira se a propriedade SelectedColor
é null
. Se for, retorne por meio do método. Caso contrário, adicione a cor selecionada à lista FavoriteColors
.
public void AddSelectedColorToFavorites()
{
if (SelectedColor == null) return;
FavoriteColors.Add(SelectedColor);
}
Quando esse método for chamado, SelectedColor
deverá ser preenchido com a cor que está sendo adicionada à lista de favoritos, mas é melhor não fazer suposições.
Com isso, concluímos o código (por enquanto). Vamos voltar nossa atenção ao XAML.
4. Alterar a ListBox
para uma ComboBox
Como queremos que a lista completa de cores seja mostrada em um menu suspenso (que é um controle ComboBox
), precisamos alterar o XAML. Felizmente, ListBox
e ComboBox
descendem do controle ItemsControl
e funcionam de forma semelhante, apesar da grande diferença na aparência e no comportamento. Tudo o que precisamos fazer é substituir ListBox
por ComboBox
no arquivo ColorList.xaml
. Você pode usar o comando Editar>Encontrar e Substituir>Substituição rápida (Ctrl+H) para realizar a substituição.
Se você executar rapidamente o aplicativo agora, verá que ListBox
foi substituído por ComboBox
, mas as cores ainda são exibidas usando o mesmo modelo.
5. Extrair o modelo para um recurso
Em relação ao modelo, você precisará reutilizá-lo na lista de cores favoritas posteriormente. É uma boa ideia armazenar o modelo em um só lugar, para que o XAML seja mais legível. Mais importante, isso garante que as alterações no modelo afetem todas as instâncias. Vamos extrair o modelo para um recurso.
Qualquer FrameworkElement
pode ter sua lista de recursos. Optamos por tornar nosso modelo global para a Window
inteira, portanto, vamos adicionar uma marca <Window.Reources>
acima do elemento <Grid>
. Em seguida, mova toda a marca <DataTemplate>
e o conteúdo dentro dela.
<Window.Resources>
<DataTemplate x:Key="ColorTemplate">
<StackPanel Orientation="Horizontal">
<Rectangle Width="80"
Height="20">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Color}"/>
</Rectangle.Fill>
</Rectangle>
<TextBlock Text="{Binding Name}"
Margin="20, 10, 0, 10"/>
</StackPanel>
</DataTemplate>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
O <Window.Resources>
é um dicionário, portanto, cada entrada também deverá ter uma chave. Nós o adicionamos ao <DataTemplate>
.
<DataTemplate x:Key="ColorTemplate">
Essa chave permitirá que façamos referência a este modelo de outro lugar dentro do Window
, como o ComboBox.ItemTemplate
, que perdeu seu conteúdo. Para que a ComboBox
use o recurso ColorDescriptor
, você pode remover a marca <ComboBox.ItemTemplate>
e usá-la como um atributo dentro da própria marca <ComboBox>
. Toda a marca <ComboBox>
tem a seguinte aparência:
<ComboBox ItemsSource="{x:Bind Logic.LotsOfColors}"
Margin="20"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ColorTemplate}"/>
Você pode executar o aplicativo novamente apenas para verificar se o modelo de item funciona.
6. Compilar o restante da interface do usuário
A interface do usuário é simples. Todos os controles (o menu suspenso, o botão Adicionar a Favoritos, a lista de favoritos (com o texto do cabeçalho) e o botão Remover dos Favoritos) ficam aninhados em um único StackPanel
vertical. Substitua todo o elemento <Grid>
pelo seguinte:
<StackPanel>
<ComboBox ItemsSource="{Binding LotsOfColors}"
Margin="20, 20, 20, 0"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ColorTemplate}"
SelectedItem="{Binding SelectedColor, Mode=TwoWay}" />
<Button
Margin="20"
HorizontalAlignment="Left">Add to Favorites</Button>
<TextBlock
FontSize="25"
Margin="20, 20, 20, 0">Favorite colors</TextBlock>
<ListBox ItemsSource="{Binding FavoriteColors}"
ItemTemplate="{StaticResource ColorTemplate}"
Margin="20, 20, 20, 0"
HorizontalAlignment="Left"/>
<Button Margin="20"
HorizontalAlignment="Left">Remove from Favorites</Button>
</StackPanel>
Há uma associação TwoWay
entre o item atualmente selecionado na ComboBox
e a propriedade SelectedColor
da classe ColorListDataContext
.
7. Criar um manipulador de cliques para o botão Add to Favorites
Já implementamos a lógica para adicionar a cor escolhida à Lista FavoriteColors
no método AddSelectedColorToFavorites
. No entanto, é necessário invocar esse método quando o usuário clica no botão.
Clique duas vezes no botão Add to Favorites
no designer visual no Visual Studio. Isso cria um método Button_Click
no code-behind e uma referência a ele no evento Click
no XAML:
<Button ... Click="Button_Click">Add to Favorites</Button>
Renomeie o método Button_Click
como AddToFavorites_Click
no code-behind e no XAML. Para fazer isso, renomeie o método no código e, usando o ícone de engrenagem, escolha "Renomear Button_Click
como AddToFavorites_Click
". Essa ação também cuidará da alteração do nome do método no arquivo XAML.
.
Adicione uma propriedade de conveniência ao início da classe ColorList
para facilitar o acesso ao ColorListDataContext
:
private ColorListDataContext DC => (ColorListDataContext)DataContext;
Em seguida, no método AddToFavorites_Click
, é preciso invocar a lógica previamente gravada na classe ColorListDataContext
:
private void AddToFavorites_Click(object sender, RoutedEventArgs e)
{
DC.AddSelectedColorToFavorites();
}
8. Execute o aplicativo
Execute o aplicativo agora, escolha uma cor em ComboBox
e selecione o botão Adicionar aos favoritos. Nada acontecerá. Adicionar um ponto de interrupção no final do método AddSelectedColorToFavorites
na classe ColorListDataContext
mostra que o código funciona. A cor selecionada é adicionada à lista FavoriteColors
.
A interface do usuário não reflete as alterações nesta List<ColorDescriptor>
porque precisa ser notificada quando a coleção é alterada. No caso das listas, isso é feito pela interface System.Collections.Specialized.INotifyCollectionChanged
. Felizmente, isso não precisa ser implementado. A classe System.Collections.ObjectModel.ObservableCollection<T>
já possui tudo aquilo de que precisamos.
Para fazer nosso aplicativo funcionar, tudo que precisamos fazer é usar a classe ObservableCollection<T>
em vez da classe List<T>
na propriedade FavoriteColors
.
public ObservableCollection<ColorDescriptor> FavoriteColors { get; } =
new ObservableCollection<ColorDescriptor>();
Se você executar o aplicativo agora, a escolha das cores no menu suspenso e o botão Adicionar a Favoritos já funcionarão corretamente. As cores favoritas escolhidas serão refletidas na ListBox
. Ótimo!
9. Evite adicionar itens vazios
Quando você inicia o aplicativo, não há cores marcadas na lista suspensa. No momento, temos uma verificação null
no método AddSelectedColorToFavorites
para impedir que itens "nulos" sejam adicionados à lista ao selecionar o botão Adicionar aos Favoritos. Vamos alterar isso para garantir que sempre haja uma cor selecionada na lista suspensa.
A verificação null
não impede que o botão Adicionar a Favoritos apareça quando não está funcional. Portanto, vamos garantir que sempre haja um item selecionado. Como a propriedade SelectedItem
do menu suspenso é associada de duas maneiras à propriedade SelectedColor
no código, vamos apenas inicializá-la com um valor válido no início. Adicione a linha a seguir no final do construtor ColorListDataContext
:
SelectedColor = LotsOfColors[0];
Isso garante que o primeiro item da lista LotsOfColors
seja selecionado quando o aplicativo for iniciado. O usuário não será capaz de adicionar um null
à coleção FavoriteColors
.
10. Remover cores favoritas
A próxima etapa é adicionar a capacidade de remover cores favoritas da ListBox
. Isso acontecerá quando o usuário escolher um item na ListBox
e selecionar o botão Remover dos Favoritos.
Com um procedimento similar ao aplicado à ComboBox
, podemos rastrear o item que o usuário escolheu na ListBox
usando a propriedade SelectedItem
. Podemos associar isso a uma propriedade no código. Adicione a nova propriedade à classe ColorListDataContext
da seguinte maneira:
private ColorDescriptor? _selectedFavoriteColor;
public ColorDescriptor? SelectedFavoriteColor
{
get => _selectedFavoriteColor;
set
{
Set(ref _selectedFavoriteColor, value);
RaisePropertyChanged(nameof(IsRemoveFavoriteEnabled));
}
}
public bool IsRemoveFavoriteEnabled => SelectedFavoriteColor != null;
O código anterior também inclui uma propriedade booliana para controlar se o botão para remover itens da lista de favoritos deve ficar visível. Qualquer alteração realizada em SelectedFavoriteColor
fará com que a interface do usuário consulte essa propriedade e aja em conformidade.
Para realmente executar a remoção da cor da lista de Favoritos, precisamos gravar mais um método.
public void RemoveFavoriteColor()
{
if (SelectedFavoriteColor == null) return;
FavoriteColors.Remove(SelectedFavoriteColor);
}
Associe o SelectedItem do ListBox
à propriedade SelectedFavoriteColor
. Adicione o atributo SelectedItem
ao ListBox
em CodeList.xaml
.
<ListBox SelectedItem="{Binding SelectedFavoriteColor, Mode=TwoWay}"... >
Para conectar o botão Remover dos Favoritos altere o XAML do botão Remover dos Favoritos para que ele inclua a associação IsEnabled
e o manipulador de eventos Click
.
<Button Margin="20"
HorizontalAlignment="Left"
Click="RemoveFromFavorites_Click"
IsEnabled="{Binding IsRemoveFavoriteEnabled}">Remove from Favorites</Button>
Também precisamos adicionar o método RemoveFromFavorites_Click
ao ColorList.xaml.cs
para chamar RemoveFromFavoriteColor
em nossa lógica.
private void RemoveFromFavorites_Click(object sender, RoutedEventArgs e)
{
DC.RemoveFavoriteColor();
}
Execute o aplicativo agora e verifique se consegue adicionar cores à lista de cores favoritas e removê-las. Observe como o botão Remover dos Favoritos fica habilitado e desabilitado de acordo com a escolha de uma cor favorita que pode ser removida.
Como exercício, tente ocultar toda a parte Cores Favoritas da interface do usuário quando a coleção FavoriteColors
estiver vazia. Dica: use um StackPanel
para agrupar os controles afetados e associar a Visibilidade de StackPanel
para uma propriedade na classe ColorListDataContext
. Sempre que uma cor favorita for adicionada ou removida, notifique a interface do usuário sobre as alterações nessa propriedade.
Resumo
Esta lição ilustrou como você pode adquirir e configurar o item escolhido em um controle ComboBox
ou ListBox
ao associar suas propriedades SelectedItem
a uma propriedade C#. Você também viu que o uso de ObservableCollection
no código faz com que a interface do usuário atualize automaticamente o conteúdo dos controles que exibem vários itens.