Изменить цвет фона строки ListView программно (wpf)
У меня есть класс, который заполняет ListView, передавая список объектов. Класс использует отражение, чтобы увидеть свойства каждого объекта, чтобы сгенерировать ListView. Как я могу изменить цвет фона строки в ListView.
Эта страница делает именно то, что я ищу. Единственная проблема заключается в том, что мой ListView привязан к списку объектов. Другими словами, каждый элемент ListView является объектом, который связан вместо ListViewItem. Я предполагаю, что это причина, почему я не могу привести какой-либо элемент в ListView к ListViewItem. Например, когда я делаю это:
ListViewItem someItem = (ListViewItem)listView1.Items[0];
Я получаю InvalidcastException, потому что если я, где физически добавить объекты в ListView, как:
listview.items.add (someObject), тогда это будет работать, но, поскольку я связываю список с ListView, эта строка не работает. Я думаю, что это причина, почему я не могу сыграть. Причина, по которой я хочу привести его, заключается в том, что ListViewItem имеет свойство Background.
РЕДАКТИРОВАТЬ
Я могу сделать это с помощью первых 12 объектов, которые я пробовал:
for (int i = 0; i < listView1.Items.Count; i++)
{
var lvitem = listView1.ItemContainerGenerator.ContainerFromIndex(i) as ListViewItem;
lvitem.Foreground = Brushes.Green;
}
и я получаю эту ошибку:
и я тоже попробовал это:
foreach (Tiro t in listView1.Items)
{
var lvitem = listView1.ItemContainerGenerator.ContainerFromItem(t) as ListViewItem;
if (t.numero == 0 || t.numero == 37)
{
//lvitem.Background = Brushes.Green;
lvitem.Foreground = Brushes.Green;
}
else if (t.numero % 2 == 0)
{
//lvitem.Background = Brushes.Red;
lvitem.Foreground = Brushes.Red;
}
else
{
//lvitem.Background = Brushes.Gray;
lvitem.Foreground = Brushes.Black;
}
}
и я получаю ту же ошибку:
Я не понимаю, почему lvitem имеет значение null после 12 итераций?
Он работает только с элементами, которые отображаются....
6 ответов
При использовании ItemContainerGenerator
затем помните, что контейнеры генерируются асинхронно. Генератор отображает событие изменения статуса, которое вы можете прослушать:
listView.ItemContainerGenerator.StatusChanged += new EventHandler(ContainerStatusChanged);
private void ContainerStatusChanged(object sender, EventArgs e)
{
if (listView.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
foreach (Tiro t in listView1.Items)
{
...
}
}
}
Не уверен, что это создаст какие-то странные эффекты рисования (мерцание) или нет.
Другой вариант, вместо создания элементов списка в коде, это использование шаблонов данных. Возможно, вам придется добавить несколько свойств в вашу модель представления для отображения.
Вам нужно ввести ViewModels вместо уничтожения пользовательского интерфейса WPF. например, я мог бы создать один следующим образом
public class ItemVM : INotifyPropertyChanged // if you want runtime changes to be reflected in the UI
{
public string Text {... raise property change in setter }
public Color BackgroundColor {... ditto... }
}
Затем создайте список таких объектов как свойство в вашем DataContext, чтобы ваш ListView мог связываться с ним.
// e.g. MainWindow
public IEnumerable<ItemVM> Items { get; set; }
Теперь все, что вам нужно сделать, это привязать свой ListView к этой коллекции и правильно подключить DataContext из пользовательского интерфейса.
<ListView x:Name="MyListView" ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}">
<TextBlock.Background>
<SolidColorBrush Color="{Binding BackgroundColor}"/>
</TextBlock.Background>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Click="Button_Click" Content="Go PaleGreen"/>
Теперь изменить цвет фона легко. Просто установите для свойства соответствующего объекта ItemVM значение Color. например, чтобы установить все элементы, чтобы перейти на PaleGreen
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach (var item in Items)
item.BackgroundColor = Colors.PaleGreen;
}
Вы могли бы использовать ItemContainerGenerator
Например:
var lvitem = listView.ItemContainerGenerator.ContainerFromItem(item) as ListViewItem;
var lvitem = listView.ItemContainerGenerator.ContainerFromIndex(0) as ListViewItem;
Однако по умолчанию ListView
виртуализируется, это означает ListViewItems
создаются "на лету" по мере необходимости (только если элемент фактически виден в списке), поэтому приведенные выше методы не будут возвращать контейнеры для элементов, которые в данный момент не видны.
В этом случае обычно предпочтительнее определить обязательную Background
собственность через Setter
в ItemContainerStyle
,
После некоторого поиска в Google я нашел свое собственное решение, которое я использую Listview.ItemsSource и в качестве источника я использую List Затем я могу установить фон для определения ListViewItem в List и просто обновить listview.
XAML:
<ListView x:Name="listView" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Row="1">
<ListView.View>
<GridView>
<GridViewColumn Header="IP" DisplayMemberBinding="{Binding IP}" Width="Auto"/>
<GridViewColumn Header="PING" DisplayMemberBinding="{Binding Ping}" Width="Auto"/>
<GridViewColumn Header="Host Name" DisplayMemberBinding="{Binding DNS}" Width="Auto"/>
<GridViewColumn Header="Mac" DisplayMemberBinding="{Binding MAC}" Width="Auto"/>
<GridViewColumn Header="Výrobce" DisplayMemberBinding="{Binding Manufacturer}" Width="Auto"/>
</GridView>
</ListView.View>
</ListView>
Заполните ListView пунктами с серым фоном:
List<ListViewItem> ITEMS = new List<ListViewItem>();
private void button_Click(object sender, RoutedEventArgs e)
{
for (int i = 1; i < 20; i++)
{
ListViewItem OneItem = new ListViewItem();
OneItem.Background = Brushes.LightGray;
OneItem.Content = new Device() { IP = "1.1.1.1", Ping = "30ms", DNS = "XYZ", MAC = "2F:3C:5F:41:F9", Manufacturer = "Intel" };
ITEMS.Add(OneItem);
listView.ItemsSource = ITEMS;
}
listView.Items.Refresh();
}
public class Device
{
public string IP { get; set; }
public string Ping { get; set; }
public string DNS { get; set; }
public string MAC { get; set; }
public string Manufacturer { get; set; }
}
Создать метод для изменения цвета строки:
private void ChangeRowColor(int RowIndex,SolidColorBrush NewBackground)
{
ITEMS[RowIndex].Background = NewBackground;
listView.Items.Refresh();
}
И использовать это:
private void button1_Click(object sender, RoutedEventArgs e)
{
ChangeRowColor(4, Brushes.Green);
}
Предполагая, что элементы в вашем ListBox имеют тип Foo, и в ListBox вы будете отображать Foo.ItemInfo для каждого элемента Foo, и, наконец, скажем, есть свойство с именем Status, которое определяет, как вы хотите, чтобы каждый Foo.ItemInfo отображался в ListBox. относительно фона, переднего плана, стиля шрифта и текста всплывающей подсказки. Исходя из этих требований, добавьте в свой XAML следующее:
<ListBox FontFamily="Courier New"
HorizontalAlignment="Left"
...
...other ListBox attributes...
...
<ListBox.Resources>
<local:BGConverter x:Key="BackgroundConverter"/>
<local:FGConverter x:Key="ForegroundConverter"/>
<local:FSConverter x:Key="FontStyleConverter"/>
<local:TTConverter x:Key="ToolTipConverter"/>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemInfo}"
Background="{Binding Converter={StaticResource BackgroundConverter}}"
FontStyle="{Binding Converter={StaticResource FontStyleConverter}}"
Foreground="{Binding Converter={StaticResource ForegroundConverter}}"
ToolTip="{Binding Converter={StaticResource ToolTipConverter}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Затем добавьте следующее в свой MainWindow.xaml.cs (или как вы назвали сопроводительный файл XAML) в C#:
public class BGConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Foo foo = (Foo)value;
string bgColor = "Gray";
switch(foo.Status)
{
case 0:
bgColor = "White";
break;
case 1:
bgColor = "Cyan";
break;
case 2:
bgColor = "Yellow";
break;
}
return bgColor;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class FSConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Foo foo = (Foo)value;
string fStyle = "Normal";
switch(foo.Status)
{
case 0:
fStyle = "Normal";
break;
case 1:
fStyle = "Oblique";
break;
case 2:
fStyle = "Italic";
break;
}
return fStyle;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class FGConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Foo foo = (Foo)value;
string fgColor = "Black";
switch(foo.Status)
{
case 0:
fgColor = "Blue";
break;
case 1:
fgColor = "Brown";
break;
case 2:
fgColor = "DarkBlue";
break;
}
return fgColor;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class TTipConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Foo foo = (Foo)value;
string ttText = "No tool tips for this item.";
switch(foo.Status)
{
case 0:
ttText = "The item has not been processed";
break;
case 1:
ttText = "The item has been processed but not saved";
break;
case 2:
ttText = "The item has been processed and saved";
break;
}
return ttText ;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Это один способ, который я нашел, который работает... нет сомнений, что есть много других способов, и ваш пробег может варьироваться...
В любом случае, HTH
List<ListViewItem> ITEMS = new List<ListViewItem>();
private void loadListView(ListView lv)
{
int numberOfRows = 20;
string[] student_number, first_name, last_name, middle_name, extension, course, year, section;
// ...... Assign values to the arrays above...
for (int h = 0; h <= numberOfRows - 1; h++)
{
ListViewItem OneItem = new ListViewItem();
OneItem.Background = course[h] == "Grade" ? Brushes.Red : Brushes.Transparent; //Decide the color of the Row
OneItem.Content = new Student
{
Student_Number = student_number[h],
Course = course[h],
Section = section[h],
Year = year[h],
FullName = first_name[h] + " " + middle_name[h] + ". " + last_name[h] + " " + extension[h]
};
ITEMS.Add(OneItem);
lv.ItemsSource = ITEMS;
}
lv.Items.Refresh();
}
public class Student
{
public string Student_Number { get; set; }
public string FullName { get; set; }
public string Course { get; set; }
public string Section { get; set; }
public string Year { get; set; }
}