Смена фона ComboBox
Возможно ли как-то простым способом у нескольких ComboBox задать уникальный цвет фона ComboBox, а так же фона выпадающего списка? Нашёл вариант со сменой системных цветов, но в этом случае цвет меняется сразу у всех, а не у одного конкретного. Или же в стандартном ComboBox такого нет и нужно писать собственный?
Ответы (1 шт):
Я покажу простой пример того, что у вас должно быть, а дальше уже адаптируйте под свои нужды.
Давайте сделаем так, чтоб у нас на экране было 2 ComboBox
, где один будет отвечать за "состояние" другого, а тот в свою очередь, на основе этих данных, менял свой цвет.
Данные
Так, как WPF это про привязки и отделение данных от UI, то мы не должны вообще хотеть делать что-то на подобии comboBox1.Backgroud = ...;
, это не правильно, ибо мы мешаем данные с UI. Ну а как нам описать данные цвета, некие "состояния"? Все очень просто, сделайте enum
, в котором будут простые значения, которые будут иметь понятные названия. Собственно, давайте так и сделаем. Пусть у нас будет enum
, назовем его Status
, ну и пусть у него будут стандартные значения "Нормально", "Успешно", "Внимание", "Ошибка", получим что-то такое:
public enum Status
{
Normal,
Success,
Warning,
Error
}
Теперь, давайте набросаем простой класс (ViewModel), который будет содержать в себе этот enum
, а также список всех значений данного enum
, ну и коллекция значений для самого ComboBox
. Так, как сам enum
(наше состояние), должно меняться во время работы программы, то он обязан вызывать INPC
, не забывайте про это, иначе UI не будет подхватывать изменения!
В итоге, у меня код будет таким:
public partial class MainViewModel : ObservableObject
{
public string[] Values => [ "Value 1", "Value 2", "Value 3", "Value 4", "Value 5" ];
public Status[] Statuses => Enum.GetValues<Status>();
[ObservableProperty]
private Status _status = Status.Normal;
}
На ObservableProperty
и ObservableObject
внимание не обращайте, это я для упрощения использую CommunityToolkit.Mvvm
, который за меня генерирует тот самый INotifyProprtyChanged
, вы вправе тут поступать как хотите, главное вызовите INPC
, а его реализация не так важна.
XAML
С данными мы закончили, теперь надо поработать с UI. Наша задача сейчас по сути простая if (Status == Warning) Background = Red;
, а какой аналог if
в XAML? Правильно, триггеры. Собственно, наша задача состоит в том, чтобы изменить стиль ComboBox
'а на тот, который будет иметь триггеры под каждое состояние. В итоге будет нечто, подобное этому:
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Statuses}" SelectedItem="{Binding Status}" />
<ComboBox ItemsSource="{Binding Values}" SelectedIndex="0">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="Success">
<Setter Property="Background" Value="{DynamicResource SystemFillColorSuccessBrush}" />
<Setter Property="Foreground" Value="{DynamicResource TextOnAccentFillColorPrimaryBrush}" />
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="Warning">
<Setter Property="Background" Value="{DynamicResource SystemFillColorCautionBrush}" />
<Setter Property="Foreground" Value="{DynamicResource TextOnAccentFillColorPrimaryBrush}" />
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="Error">
<Setter Property="Background" Value="{DynamicResource SystemFillColorCriticalBrush}" />
<Setter Property="Foreground" Value="{DynamicResource TextOnAccentFillColorPrimaryBrush}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</StackPanel>
Опять, не обращайте внимание на всякие TextOnAccentFillColorPrimaryBrush
, я использую свои цвета, у вас тут естественно будут свои. Также заметьте, я в триггере указываю все нужные значения для изменения.
Так вот, если все сделали правильно, запускаем, смотрим результат:
Что-то не так, да? Наш контрл потерял стандартные стили, но почему? Все дело в том, что мы сделали сейчас по сути что-то такое comboBox1.Style = новый стиль
, а сам "новый стиль" пустой, он не имеет в себе ничего. Как поступить в ситуации, когда нужно дополнить базовый класс? Правильно, если позволяют, наследуем, то есть делаем новый стиль : старый стиль
. В XAML тоже можно наследовать стили, делается это через свойство BaseOn
. Собственно берем и дописываем в <Style TargetType="ComboBox">
это свойство, указывая тот ресурс (название/тип), от которого надо унаследовать. Так, как нам нужен стандартный, то нам надо привязаться на нужный тип через StaticResource
, а именно BasedOn="{StaticResource {x:Type ComboBox}}"
.
Запускаем проект повторно и о чудо, все сработало:
Переключаем теперь состояние, любуемся результатом:
Как видите, все работает, XAML автоматически подстраивается под значение привязанного свойства.
Но только учтите, если вы дополняете базовый стиль, то стоит изучить его, иначе вы можете что-то упустить и в итоге получите разные "глюки", как, например, у меня, где при смене цвета не меняется цвет при наведении курсора:
Но это уже совсем другая история...
Вот собственно базовый, да и один из самых простых (из правильных) способов изменить что-то в UI на основе чего-то в данных. Дальше уже подстраивайте под свой проект, в вопросе вон вижу вы про фон выпадающего списка говорите, вот попробуйте по аналогии написать стиль и для него.
Я кстати, немного не до конца понял ваш вопрос, а именно "уникальный цвет". Если вам надо делать прям совсем уникальный цвет для каждого созданного ComboBox
, то "цвет" в данном случае, это уже конкретные данные, конкретного объекта, а значит тут нужно создавать класс, который опишет конкретный объект и в нем хранить свойство "цвет", ну а дальше просто привязываете это свойство к ComboBox
и готово. Правда как по мне, делать "уникальный" цвет каждому UI компоненту, это уже плохой UX приложения, который только отвлечет пользователя.