Обновление пользовательского интерфейса при изменении коллекций
В этом уроке пользователь выберет любимые цвета. Доступные цвета перечислены в раскрывающемся списке (элемент управления ComboBox
). Пользователь выбирает цвет и добавляет его в избранное, нажав кнопку. Любимые цвета показаны ниже. При выборе любимого цвета также отображается кнопка, которая позволяет пользователю удалить выбранный цвет из избранного.
Сначала предоставьте возможность выбрать цвет из коллекции LotsOfColors
и добавить его в список избранных цветов.
1. Создание SelectedColor
свойства
Начнем с кода, а затем перейдем к пользовательскому интерфейсу.
Нам нужен способ, чтобы определить, какой элемент (то есть экземпляр класса ColorDescriptor
) выбрал пользователь из раскрывающегося списка. Элемент управления ComboBox
имеет свойство SelectedItem
, которое получает и задает текущий выбранный элемент. Таким образом, мы можем привязать это свойство к свойству типа ColorDescriptor
в нашем коде.
Откройте ColorListLogic.cs и добавьте следующий код:
private ColorDescriptor _selectedColor;
public ColorDescriptor SelectedColor
{
get => _selectedColor;
set => Set(ref _selectedColor, value);
}
Этот шаблон уже должен быть вам знаком. Это стандартное свойство, дополненное механизмом INotifyPropertyChanged
с использованием базового класса ObservableObject
.
2. Создание FavoriteColors
списка
В списке FavoriteColors
хранятся цвета, выбранные пользователем в качестве избранных. Это простое свойство.
public List<ColorDescriptor> FavoriteColors { get; } =
new List<ColorDescriptor>();
3. Добавление выбранного цвета в избранное
Добавление выбранного цвета в избранное происходит в методе AddSelectedColorToFavorites
.
public void AddSelectedColorToFavorites()
{
FavoriteColors.Add(SelectedColor);
}
Предполагается, что при вызове этого метода SelectedColor
заполняется цветом, который должен быть добавлен в список избранных.
На этом работа с кодом закончена (пока). Давайте обратим внимание на XAML.
4. Изменение на ListBox
ComboBox
Так как мы хотим иметь полный список цветов, отображаемых в раскрывающемся списке (который является элементом ComboBox
управления), нам нужно изменить XAML. К счастью, и ListBox
, и ComboBox
являются потомками элемента управления ItemsControl
и работают похоже, несмотря на то, что есть различия в том, как они выглядят и функционируют. Нам нужно всего лишь заменить ListBox
на ComboBox
в файле ColorList.xaml. Для этого можно использовать команду Правка>Найти и заменить>Быстрая замена (CTRL+H).
Если вы сейчас запустите приложение, то увидите, что ListBox
заменен на ComboBox
, но цвета по-прежнему отображаются с использованием того же шаблона.
5. Извлечение шаблона в ресурс
Вам нужно будет повторно использовать шаблон для списка любимых цветов позже. Советуем хранить шаблон в одном расположении, чтобы улучшить читаемость XAML. Важнее то, что в этом случае изменения шаблона влияют на каждый экземпляр. Давайте извлечем шаблон в ресурс.
Для любого элемента FrameworkElement
может быть определен свой список ресурсов. Мы решили сделать наш шаблон глобальным для этой страницы, поэтому давайте добавим тег <Page.Reources>
над элементом <Grid>
. Затем переместим в него весь тег <DataTemplate>
и его содержимое.
<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>
Visual Studio предупреждает о том, что объекты в теге <Page.Resources>
(который является IDictionary
) должны иметь атрибут Key
, поэтому добавьте его в <DataTemplate>
.
<DataTemplate x:Key="ColorTemplate"
x:DataType="local:ColorDescriptor">
Этот ключ позволяет нам ссылаться на этот шаблон из другого расположения на странице, например на шаблон ComboBox.ItemTemplate
, который потерял свое содержимое. Чтобы ComboBox
использовал ресурс ColorDescriptor
, можно удалить тег <ComboBox.ItemTemplate>
и указать его в качестве атрибута внутри самого тега <ComboBox>
. Весь тег <ComboBox>
выглядит следующим образом:
<ComboBox ItemsSource="{x:Bind Logic.LotsOfColors}"
Margin="20"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ColorTemplate}"/>
Вы можете снова запустить приложение, чтобы убедиться, что шаблон элемента работает.
6. Создание остальной части пользовательского интерфейса
Пользовательский интерфейс прост. Все элементы управления (раскрывающийся список, кнопка Добавить в избранное, список избранных (с текстом заголовка) и кнопка Удалить из избранного) размещаются в одном вертикальном элементе StackPanel
. Замените все содержимое элемента <Grid>
следующим:
<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>
Существует привязка TwoWay
между текущим выбранным элементом в ComboBox
и свойством SelectedColor
в классе ColorListLogic
.
7. Запуск приложения
Запустите приложение сейчас, выберите цвет из ComboBox
и нажмите кнопку Добавить в избранное. Ничего не происходит. Добавление точки останова в конец метода AddSelectedColorToFavorites
в классе ColorListLogic
показывает, что код работает. Выбранный цвет добавляется в список FavoriteColors
.
Причина, по которой пользовательский интерфейс не отражает изменения в элементе List<ColorDescriptor>
, заключается в том, что интерфейс должен получать уведомление при изменении коллекции. Для списков это делается через интерфейс System.Collections.Specialized.INotifyCollectionChanged
. К счастью, нам не нужно это реализовывать. В классе System.Collections.ObjectModel.ObservableCollection<T>
уже есть все, что нам нужно.
Чтобы наше приложение работало, нам нужно лишь использовать для свойства FavoriteColors
класс ObservableCollection<T>
вместо класса List<T>
.
public ObservableCollection<ColorDescriptor> FavoriteColors { get; } =
new ObservableCollection<ColorDescriptor>();
Если вы запустите приложение сейчас, то выбор цветов из раскрывающегося списка и нажатие кнопки Добавить в избранное будут работать правильно. Выбранные цвета будут отображены в элементе ListBox
. Отлично!
8. Избегайте добавления пустых элементов
При запуске приложения в раскрывающемся списке не выбран цвет. Если вы сейчас нажмете кнопку Add to Favorites (Добавить в избранное), в список будут добавлены значения null
. Это ошибка, так что давайте исправим ее.
Мы могли бы добавить проверку null
в метод AddSelectedColorToFavorites
, но это не помешает кнопке Добавить в избранное отображаться, когда она не является функциональной. Вместо этого давайте сделаем так, чтобы в раскрывающемся списке всегда был выбран элемент. Так как свойство SelectedItem
раскрывающегося списка имеет двустороннюю привязку к свойству SelectedColor
в коде, давайте просто инициализируем его допустимым значением при запуске. Добавьте следующую строку в конце конструктора ColorListLogic
:
SelectedColor = LotsOfColors[0];
Это гарантирует, что первый элемент списка LotsOfColors
будет выбран при запуске приложения. Пользователь не сможет добавить null
в коллекцию FavoriteColors
.
9. Удаление избранных цветов
Далее следует добавить возможность удалять избранные цвета из элемента ListBox
. Это происходит, когда пользователь выбирает элемент в ListBox
и нажимает кнопку Remove from Favorites (Удалить из избранного).
Подобно тому, как работал ComboBox
, мы можем отслеживать элемент, выбранный пользователем в ListBox
, через его свойство SelectedItem
. Мы можем привязать это к свойству в коде. Добавьте это в класс ColorListLogic
.
private ColorDescriptor _selectedFavoriteColor;
public ColorDescriptor SelectedFavoriteColor
{
get => _selectedFavoriteColor;
set
{
Set(ref _selectedFavoriteColor, value);
RaisePropertyChanged(nameof(IsRemoveFavoriteColorButtonVisible));
}
}
public bool IsRemoveFavoriteColorButtonVisible => SelectedFavoriteColor != null;
Предыдущий код также содержит логическое свойство, которое управляет отображением кнопки для удаления элементов из списка избранного. При любом изменении SelectedFavoriteColor
пользовательский интерфейс запрашивает это свойство и действует соответственно.
Чтобы реально выполнить удаление цвета из списка избранного, нужно написать еще один метод.
public void RemoveFavoriteColor()
{
FavoriteColors.Remove(SelectedFavoriteColor);
}
Чтобы подключить кнопку в коде XAML, откройте файл ColorList.xaml и измените код XAML кнопки Удалить из избранного Измените ее таким образом, чтобы она включает привязку Visibility
, а также привязку Click
.
<Button Margin="20"
Visibility="{x:Bind Logic.IsRemoveFavoriteColorButtonVisible, Mode=OneWay}"
Click="{x:Bind Logic.RemoveFavoriteColor}">Remove from Favorites</Button>
Осталось только привязать свойство SelectedItem
элемента ListBox
к свойству Logic.SelectedFavoriteColor
. Добавьте атрибут SelectedItem
в ListBox
в коде XAML.
<ListBox SelectedItem="{x:Bind Logic.SelectedFavoriteColor, Mode=TwoWay}"... >
Запустите приложение сейчас и убедитесь, что вы можете добавлять цвета в список избранных, а также удалять их. Обратите внимание, как кнопка Удалить из избранного появляется и исчезает в зависимости от того, выбран ли избранный цвет, который можно удалить.
Итоги
В этом уроке показано, как получить и установить выбранный элемент в элементе управления ComboBox
или ListBox
, связав его свойства SelectedItem
со свойством C#. Вы также видели, как при использовании ObservableCollection
в коде пользовательский интерфейс автоматически обновляет содержимое элементов управления, отображающих несколько элементов.
На этом занятии пользователь выберет свои любимые цвета. Доступные цвета перечислены в раскрывающемся списке (элемент управления ComboBox
). Пользователь выбирает цвет и добавляет его в избранное, нажав кнопку. Избранные цвета отображаются под полным списком. При выборе любимого цвета также отображается кнопка, которая позволяет пользователю удалить выбранный цвет из избранного.
Сначала мы добавим возможность выбрать цвет из LotsOfColors
коллекции и добавить его в список любимых цветов.
1. Создание SelectedColor
свойства
Начнем с кода, а затем мы будем работать с изменениями пользовательского интерфейса.
Нам нужен способ, чтобы определить, какой элемент (то есть экземпляр класса ColorDescriptor
) выбрал пользователь из раскрывающегося списка. Элемент управления ComboBox
имеет свойство SelectedItem
, которое получает и задает текущий выбранный элемент. Таким образом, мы можем привязать это свойство к свойству типа ColorDescriptor
в нашем коде.
Откройте ColorListDataContext.cs
и добавьте следующий код:
private ColorDescriptor? _selectedColor;
public ColorDescriptor? SelectedColor
{
get => _selectedColor;
set => Set(ref _selectedColor, value);
}
Этот шаблон уже должен быть вам знаком. Это стандартное свойство, которое использует INotifyPropertyChanged
механизм с помощью ObservableObject
базового класса.
2. Создание FavoriteColors
списка
В списке FavoriteColors
хранятся цвета, которые пользователь выбрал в качестве избранных. Это простое свойство.
public List<ColorDescriptor> FavoriteColors { get; } =
new List<ColorDescriptor>();
3. Добавление выбранного цвета в избранное
Добавление выбранного цвета в избранное происходит в методе AddSelectedColorToFavorites
. В качестве меры предосторожности проверка, если SelectedColor
это свойствоnull
. Если это так, вернитесь из метода. В противном случае добавьте выбранный цвет в FavoriteColors
список.
public void AddSelectedColorToFavorites()
{
if (SelectedColor == null) return;
FavoriteColors.Add(SelectedColor);
}
При вызове SelectedColor
этого метода следует заполнить цветом, добавленным в список избранного, но лучше не делать предположений.
На этом работа с кодом закончена (пока). Давайте обратим внимание на XAML.
4. Изменение на ListBox
ComboBox
Так как мы хотим иметь полный список цветов, отображаемых в раскрывающемся списке (который является элементом ComboBox
управления), нам нужно изменить XAML. К счастью, оба ListBox
и ComboBox
являются потомками ItemsControl
контроля, и они работают аналогично, несмотря на то, что существуют многочисленные различия в том, как они выглядят и ведут себя. Все, что нам нужно сделать, — заменить ListBox
его ComboBox
в ColorList.xaml
файле. Для этого можно использовать команду Правка>Найти и заменить>Быстрая замена (CTRL+H).
Если вы сейчас запустите приложение, то увидите, что ListBox
заменен на ComboBox
, но цвета по-прежнему отображаются с использованием того же шаблона.
5. Извлечение шаблона в ресурс
Вам нужно будет повторно использовать шаблон для списка любимых цветов позже. Советуем хранить шаблон в одном расположении, чтобы улучшить читаемость XAML. Важнее то, что в этом случае изменения шаблона влияют на каждый экземпляр. Давайте извлечем шаблон в ресурс.
Для любого элемента FrameworkElement
может быть определен свой список ресурсов. Мы решили сделать наш шаблон глобальным для всего элемента Window
, поэтому давайте добавим тег <Window.Reources>
над элементом <Grid>
. Затем переместим в него весь тег <DataTemplate>
и его содержимое.
<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>
<Window.Resources>
является словарем, поэтому каждая запись также должна иметь ключ. Мы добавили его в <DataTemplate>
.
<DataTemplate x:Key="ColorTemplate">
Этот ключ позволяет нам ссылаться на этот шаблон из другого расположения в элементе Window
, например из элемента ComboBox.ItemTemplate
, который потерял свое содержимое. Чтобы ComboBox
использовал ресурс ColorDescriptor
, можно удалить тег <ComboBox.ItemTemplate>
и указать его в качестве атрибута внутри самого тега <ComboBox>
. Весь тег <ComboBox>
выглядит следующим образом:
<ComboBox ItemsSource="{x:Bind Logic.LotsOfColors}"
Margin="20"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ColorTemplate}"/>
Вы можете снова запустить приложение, чтобы убедиться, что шаблон элемента работает.
6. Создание остальной части пользовательского интерфейса
Пользовательский интерфейс прост. Все элементы управления (раскрывающийся список, кнопка "Добавить в избранное", список избранного (с текстом заголовка) и кнопка "Удалить из избранного") вложены в одну вертикальную.StackPanel
Замените весь элемент <Grid>
следующим кодом:
<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>
Существует привязка TwoWay
между текущим выбранным элементом в ComboBox
и свойством SelectedColor
в классе ColorListDataContext
.
7. Создание обработчика нажатия кнопки Add to Favorites
Мы уже реализовали логику добавления выбранного цвета в список FavoriteColors
в методе AddSelectedColorToFavorites
. Однако нам нужно, чтобы этот метод вызывался при нажатии кнопки.
Дважды нажмите кнопку Add to Favorites
в визуальном конструкторе Visual Studio. При этом создается Button_Click
метод в коде программной части и ссылка на него в Click
событии в XAML:
<Button ... Click="Button_Click">Add to Favorites</Button>
Переименуйте метод Button_Click
в AddToFavorites_Click
как в коде программной части, так и в XAML. Это можно сделать, переименовав его в коде, а затем используя значок отвертки, выбрав "Переименовать Button_Click
в AddToFavorites_Click
". Это также позволит изменить имя метода в файле XAML.
.
Добавьте свойство для удобства в начало класса ColorList
, чтобы упростить доступ к классу ColorListDataContext
.
private ColorListDataContext DC => (ColorListDataContext)DataContext;
Затем в методе AddToFavorites_Click
вызовите ранее записанную логику из класса ColorListDataContext
:
private void AddToFavorites_Click(object sender, RoutedEventArgs e)
{
DC.AddSelectedColorToFavorites();
}
8. Запуск приложения
Запустите приложение сейчас, выберите цвет из ComboBox
и нажмите кнопку Добавить в избранное. Ничего не происходит. Добавление точки останова в конец метода AddSelectedColorToFavorites
в классе ColorListDataContext
показывает, что код работает. Выбранный цвет добавляется в список FavoriteColors
.
Причина, по которой пользовательский интерфейс не отражает изменения в элементе List<ColorDescriptor>
, заключается в том, что интерфейс должен получать уведомление при изменении коллекции. Для списков это делается через интерфейс System.Collections.Specialized.INotifyCollectionChanged
. К счастью, нам не нужно это реализовывать. В классе System.Collections.ObjectModel.ObservableCollection<T>
уже есть все, что нам нужно.
Чтобы наше приложение работало, нам нужно лишь использовать для свойства FavoriteColors
класс ObservableCollection<T>
вместо класса List<T>
.
public ObservableCollection<ColorDescriptor> FavoriteColors { get; } =
new ObservableCollection<ColorDescriptor>();
Если вы запустите приложение сейчас, то выбор цветов из раскрывающегося списка и нажатие кнопки Добавить в избранное будут работать правильно. Выбранные цвета будут отображены в элементе ListBox
. Отлично!
9. Избегайте добавления пустых элементов
При запуске приложения в раскрывающемся списке не выбран цвет. В настоящее время у нас есть null
проверка в AddSelectedColorToFavorites
методе, чтобы предотвратить добавление элементов NULL в список при нажатии кнопки "Добавить в избранное". Давайте изменим это, чтобы убедиться, что в раскрывающемся списке всегда выбран цвет.
Проверка null
не препятствует отображению кнопки "Добавить в избранное", когда она не работает. Итак, давайте просто убедитесь, что есть всегда выбранный элемент. Так как свойство SelectedItem
раскрывающегося списка имеет двустороннюю привязку к свойству SelectedColor
в коде, давайте просто инициализируем его допустимым значением при запуске. Добавьте следующую строку в конце конструктора ColorListDataContext
:
SelectedColor = LotsOfColors[0];
Это гарантирует, что первый элемент списка LotsOfColors
будет выбран при запуске приложения. Пользователь не сможет добавить null
в коллекцию FavoriteColors
.
10. Удаление избранных цветов
Далее следует добавить возможность удалять избранные цвета из элемента ListBox
. Это происходит, когда пользователь выбирает элемент в ListBox
и нажимает кнопку Удалить из избранного.
Подобно тому, как работал ComboBox
, мы можем отслеживать элемент, выбранный пользователем в ListBox
, через его свойство SelectedItem
. Мы можем привязать это к свойству в коде. Добавьте новое свойство в ColorListDataContext
класс следующим образом:
private ColorDescriptor? _selectedFavoriteColor;
public ColorDescriptor? SelectedFavoriteColor
{
get => _selectedFavoriteColor;
set
{
Set(ref _selectedFavoriteColor, value);
RaisePropertyChanged(nameof(IsRemoveFavoriteEnabled));
}
}
public bool IsRemoveFavoriteEnabled => SelectedFavoriteColor != null;
Предыдущий код также содержит логическое свойство, которое управляет отображением кнопки для удаления элементов из списка избранного. При любом изменении SelectedFavoriteColor
пользовательский интерфейс запрашивает это свойство и действует соответственно.
Чтобы реально выполнить удаление цвета из списка избранного, нужно написать еще один метод.
public void RemoveFavoriteColor()
{
if (SelectedFavoriteColor == null) return;
FavoriteColors.Remove(SelectedFavoriteColor);
}
Затем привяжите свойство SelectedItem элемента ListBox
к свойству SelectedFavoriteColor
. Добавьте атрибут в SelectedItem
ListBox
CodeList.xaml
.
<ListBox SelectedItem="{Binding SelectedFavoriteColor, Mode=TwoWay}"... >
Чтобы подключить кнопку "Удалить из избранного ", измените XAML кнопки "Удалить из избранного ", чтобы она включает IsEnabled
привязку, а также Click
обработчик событий.
<Button Margin="20"
HorizontalAlignment="Left"
Click="RemoveFromFavorites_Click"
IsEnabled="{Binding IsRemoveFavoriteEnabled}">Remove from Favorites</Button>
Для вызова RemoveFromFavoriteColor
в логику также необходимо добавить RemoveFromFavorites_Click
методColorList.xaml.cs
.
private void RemoveFromFavorites_Click(object sender, RoutedEventArgs e)
{
DC.RemoveFavoriteColor();
}
Запустите приложение сейчас и убедитесь, что вы можете добавлять цвета в список избранных, а также удалять их. Обратите внимание, как кнопка Удалить из избранного включается и отключается в зависимости от того, выбран ли избранный цвет, который можно удалить.
В качестве упражнения попробуйте скрыть всю часть с избранными цветами в пользовательском интерфейсе, когда коллекция FavoriteColors
пуста. Подсказка. Используйте StackPanel
, чтобы сгруппировать затронутые элементы управления, и привяжите условия видимости StackPanel
к свойству в классе ColorListDataContext
. При добавлении или удалении избранного цвета уведомляйте пользовательский интерфейс об изменениях этого свойства.
Итоги
В этом уроке показано, как получить и установить выбранный элемент в элементе управления ComboBox
или ListBox
, связав его свойство SelectedItem
со свойством C#. Вы также видели, как при использовании ObservableCollection
в коде пользовательский интерфейс автоматически обновляет содержимое элементов управления, отображающих несколько элементов.