Пожалуйста, смотрите следующий код.
Создает ListBox
с пятью пунктами. Выбранный элемент ListBox
окрашен в желтый цвет, предыдущие элементы (индекс под выбранным индексом) окрашены в зеленый цвет, а будущие элементы (индекс над выбранным индексом) окрашены в красный цвет.
ItemViewModel.vb
Public Class ItemViewModel
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _title As String
Private _isOld As Boolean
Private _isNew As Boolean
Protected Overridable Sub OnPropertyChanged(<CallerMemberName> Optional propertyName As String = Nothing)
If String.IsNullOrEmpty(propertyName) Then
Exit Sub
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Public Property Title As String
Get
Return _title
End Get
Set(value As String)
_title = value
Me.OnPropertyChanged()
End Set
End Property
Public Property IsOld As Boolean
Get
Return _isOld
End Get
Set(value As Boolean)
_isOld = value
Me.OnPropertyChanged()
End Set
End Property
Public Property IsNew As Boolean
Get
Return _isNew
End Get
Set(value As Boolean)
_isNew = value
Me.OnPropertyChanged()
End Set
End Property
End Class
MainViewModel:
Public Class MainViewModel
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private ReadOnly _items As ObservableCollection(Of ItemViewModel)
Private _selectedIndex As Integer
Public Sub New()
_items = New ObservableCollection(Of ItemViewModel)
_items.Add(New ItemViewModel With {.Title = "Very old"})
_items.Add(New ItemViewModel With {.Title = "Old"})
_items.Add(New ItemViewModel With {.Title = "Current"})
_items.Add(New ItemViewModel With {.Title = "New"})
_items.Add(New ItemViewModel With {.Title = "Very new"})
Me.SelectedIndex = 0
End Sub
Protected Overridable Sub OnPropertyChanged(<CallerMemberName> Optional propertyName As String = Nothing)
If String.IsNullOrEmpty(propertyName) Then
Exit Sub
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Public ReadOnly Property Items As ObservableCollection(Of ItemViewModel)
Get
Return _items
End Get
End Property
Public Property SelectedIndex As Integer
Get
Return _selectedIndex
End Get
Set(value As Integer)
_selectedIndex = value
Me.OnPropertyChanged()
For index As Integer = 0 To Me.Items.Count - 1
Me.Items(index).IsOld = (index < Me.SelectedIndex)
Me.Items(index).IsNew = (index > Me.SelectedIndex)
Next index
End Set
End Property
End Class
MainWindow.xaml
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="200">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<ListBox ItemsSource="{Binding Items}" SelectedIndex="{Binding SelectedIndex}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsOld}" Value="True">
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True">
<Setter Property="Foreground" Value="Yellow" />
</DataTrigger>
<DataTrigger Binding="{Binding IsNew}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
Это работает, как и ожидалось, но мне не нравится, что ItemViewModel
содержит свойстваIsOld
и IsNew
и что MainViewModel
отвечает за обновление этих свойств. По моему мнению, это должно быть сделано ListBox
, а не каждой моделью представления, которая может быть DataContext
для моего ListBox
.
Я уже пытался создать два прикрепленных свойства для ListBoxItem
ипривязать к ним (как я привязан к IsSelected
для текущего элемента). Но я не смог выяснить событие, при котором я обновляю эти прикрепленные свойства.
Можно ли использовать эти присоединенные свойства? Когда и / или где я могу обновить эти прикрепленные свойства? Я попытался присоединиться к событию ValueChanged
свойства ItemsSource
ListBox
, чтобы иметь возможность присоединиться к событию CollectionChanged
базовой коллекции. Но мне не удалось получить ListBoxItem
для элемента, так как эти контейнеры создаются асинхронно (поэтому я предполагаю). А поскольку ListBox
по умолчанию использует VirtualizingStackPanel
, я бы не получил ListBoxItem
для каждого элемента моей базовой коллекции.
Пожалуйста, имейте в виду, что коллекция элементов, с которыми я связываюсьнаблюдаема и может измениться. Поэтому свойства IsOld
и IsNew
необходимо обновлять при каждом изменении самой исходной коллекции, при изменении содержимого исходной коллекции и при изменении выбранного индекса.
Или как еще можно добиться того, что яхотел бы достичь?
Я специально не отмечал VB.net, поскольку этот вопрос не имеет никакого отношения к VB.net, и я в порядке с ответами на C #.
Спасибо.