Смена фона ComboBox

Возможно ли как-то простым способом у нескольких ComboBox задать уникальный цвет фона ComboBox, а так же фона выпадающего списка? Нашёл вариант со сменой системных цветов, но в этом случае цвет меняется сразу у всех, а не у одного конкретного. Или же в стандартном ComboBox такого нет и нужно писать собственный?


Ответы (1 шт):

Автор решения: EvgeniyZ

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

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

Result 1

Что-то не так, да? Наш контрл потерял стандартные стили, но почему? Все дело в том, что мы сделали сейчас по сути что-то такое comboBox1.Style = новый стиль, а сам "новый стиль" пустой, он не имеет в себе ничего. Как поступить в ситуации, когда нужно дополнить базовый класс? Правильно, если позволяют, наследуем, то есть делаем новый стиль : старый стиль. В XAML тоже можно наследовать стили, делается это через свойство BaseOn. Собственно берем и дописываем в <Style TargetType="ComboBox"> это свойство, указывая тот ресурс (название/тип), от которого надо унаследовать. Так, как нам нужен стандартный, то нам надо привязаться на нужный тип через StaticResource, а именно BasedOn="{StaticResource {x:Type ComboBox}}".
Запускаем проект повторно и о чудо, все сработало:

Result 2

Переключаем теперь состояние, любуемся результатом:

Success Warning Error

Как видите, все работает, XAML автоматически подстраивается под значение привязанного свойства.
Но только учтите, если вы дополняете базовый стиль, то стоит изучить его, иначе вы можете что-то упустить и в итоге получите разные "глюки", как, например, у меня, где при смене цвета не меняется цвет при наведении курсора:

XAML Issue

Но это уже совсем другая история...
Вот собственно базовый, да и один из самых простых (из правильных) способов изменить что-то в UI на основе чего-то в данных. Дальше уже подстраивайте под свой проект, в вопросе вон вижу вы про фон выпадающего списка говорите, вот попробуйте по аналогии написать стиль и для него.

Я кстати, немного не до конца понял ваш вопрос, а именно "уникальный цвет". Если вам надо делать прям совсем уникальный цвет для каждого созданного ComboBox, то "цвет" в данном случае, это уже конкретные данные, конкретного объекта, а значит тут нужно создавать класс, который опишет конкретный объект и в нем хранить свойство "цвет", ну а дальше просто привязываете это свойство к ComboBox и готово. Правда как по мне, делать "уникальный" цвет каждому UI компоненту, это уже плохой UX приложения, который только отвлечет пользователя.

→ Ссылка