MVVM обновляет фоновый цвет элемента списка с помощью команды кнопки

Я пытаюсь обновить фон элемента Listbox независимо, используя следующую команду: Модель

public class Item
{
    public Item()
    {
        this.BackColor = new SolidColorBrush(Colors.WhiteSmoke);
    }

    public int Number { get; set; }
    public int Duration { get; set; }
    public string Name { get { return Number.ToString(); } }
    public SolidColorBrush BackColor { get; set; }
}

ViewModel

private ObservableCollection<Item> _items;

public ObservableCollection<Item> Items
{
    get { return _items; }
    set { OnPropertyChange(ref _items, value); }
}

Кнопочная команда

public ICommand StartCommand { get; set; }

public void start()
{
    foreach (var item in Items
    {
        item.BackColor = new SolidColorBrush(Colors.LightGreen);
        // Do some work .....
    }
}

КТОР

public MainViewModel()
{
    StartCommand = new RelayCommand(start);
    LoadItems();
}

Xaml

<ListBox  ItemsSource="{Binding Items}" >
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Border 
                Height="50" 
                BorderThickness="1" 
                BorderBrush="Silver" 
                CornerRadius="5" 
                Margin="10,10,10,5" 
                Background="{Binding BackColor,
                UpdateSourceTrigger=PropertyChanged}">
            </Border>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Я могу обновить наблюдаемые данные коллекции, удалить элементы, но не изменить фон элемента. Пожалуйста, помогите.

«Обновление»: как видите, я не хочу менять цвет всего списка, поэтому я не могу привязываться к общему свойству, например «ItemBackground», вместо этого я должен привязываться к свойству объекта коллекции «BackColor».


person Emad Ali    schedule 12.11.2017    source источник
comment
Я думаю, что это другое. потому что я не пытаюсь изменить цвет фона для всего списка элементов. Мне нужно изменить цвет для каждого элемента независимо   -  person Emad Ali    schedule 12.11.2017
comment
Вам нужно реализовать INotifyPropertyChanged в вашем классе Item.   -  person Fruchtzwerg    schedule 12.11.2017
comment
@Fruchtzwerg прав, вам просто не хватает реализации INotifyPropertyChanged. Однако, поскольку вы используете mvvm, вам следует пересмотреть возможность сохранения фона как Brush внутри вашей модели. В mvvm модель ничего не должна знать о представлении, а Brush во многом является частью представления. Попробуйте вместо этого сохранить некоторое состояние и использовать конвертер, чтобы изменить его на фон.   -  person CKII    schedule 12.11.2017
comment
@CKII Здесь на SO постоянно обсуждается вопрос о том, нарушает ли использование типов фреймворков, таких как Brush в модели представления, MVVM или нет. Конечно, использование типа Brush не означает, что модель представления знает представление, как вы говорите. Однако это может избежать повторного использования представления, в котором используется другой тип кисти. Но это, конечно, очень маловероятно. IMO, использование любого типа, который не является производным от UIElement, является совершенно допустимым типом модели представления, таким как Color, Brush, Pen, Geometry, ImageSource и т. д.   -  person Clemens    schedule 12.11.2017
comment
@EmadAli Обратите внимание, что установка UpdateSourceTrigger=PropertyChanged в фоновой привязке бессмысленна. Он действует только в привязках TwoWay или OneWayToSource. Также обратите внимание, что вместо new SolidColorBrush(Colors.LightGreen) вы могли бы также написать Brushes.LightGreen.   -  person Clemens    schedule 12.11.2017
comment
@Clemens Я думаю, это решение во многом основано на личных предпочтениях. Но, во-первых, мы говорим здесь о модели, а не о виртуальной машине. Я понимаю, что правила для ВМ могут сильно различаться, но для меня модель должна оставаться максимально чистой. Во-вторых, мне не нравится помещать что-либо из пространств имен System.Windows.*, Windows.* и Microsoft.* в модель и виртуальную машину. Поработав в кроссплатформенной разработке, я могу сказать вам, что использовать эти элементы там очень больно.   -  person CKII    schedule 12.11.2017
comment
@CKII Item здесь является классом модели представления. Неважно, как это называет ОП.   -  person Clemens    schedule 12.11.2017
comment
Спасибо Всем за ваши комментарии. @Fruchtzwerg Мне действительно нужно реализовать INotifyPropertyChange как для модели элемента, так и для моей модели представления?   -  person Emad Ali    schedule 12.11.2017
comment
Обычно вы должны реализовать его в модели и модели представления. Поскольку никакое свойство в вашей модели представления не изменяется, оно также будет работать без его реализации.   -  person Fruchtzwerg    schedule 12.11.2017


Ответы (1)


Как было предложено в связанном сообщении @pix, вам не хватает реализации интерфейса INotifyPropertyChanged для вашего класса предметов. Без INotifyPropertyChanged ваш пользовательский интерфейс не будет получать обновления внесенных вами изменений.

public class Item : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Затем ваше свойство BackColor должно использовать OnPropertyChanged:

private SolidColorBrush backColor;

public SolidColorBrush BackColor
{
    get { return backColor; }
    set
    {
        backColor = value;
        OnPropertyChanged(nameof(BackColor));
    }
}
person Andreas    schedule 12.11.2017
comment
Почему вы объявляете CallerMemberName, когда вы его не используете? Тем не менее, если вы внимательно прочитаете вопрос, вы поймете, что у OP уже есть другая модель представления, которая реализует INotifyPropertyChanged. Кажется, нет необходимости объяснять, как это реализовано. - person Clemens; 12.11.2017
comment
@Clemens Просто для того, чтобы более четко показать, что PropertyChanged используется со свойством BackColor. Это действительно не нужно. - person Andreas; 12.11.2017
comment
Я уже реализовал INotifyPropertyChange в своей модели представления, а не в модели элемента. Также я думаю, что установка свойства для заднего цвета повлияет на все элементы списка, а не на текущий элемент в моем цикле foreach. Я прав ? - person Emad Ali; 12.11.2017
comment
Ты не прав. Свойство BackgroundColor является частью вашего класса Item. Это означает, что каждый элемент, который вы добавляете в свой список, будет иметь свой собственный цвет. Кроме того, чтобы ваш пользовательский интерфейс реагировал на изменения в самом классе элементов, вам необходимо реализовать INotifyPropertyChanged. - person Andreas; 12.11.2017