I have a class that populates a ListView by passing a list of objects. The class uses reflection to see the properties of each object in order to generate the ListView. How
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; }
}
output screenshot
Assuming the items in your ListBox are of type Foo, and in the ListBox you will display Foo.ItemInfo for each Foo item, and finally let's say there's a property called Status that determines how you want each Foo.ItemInfo to be displayed in the ListBox with respect to the background, foreground, font style, and tooltip text. Based on these requirements, add the following in your 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>
Next, add the following into your MainWindow.xaml.cs (or whatever you've named the XAML's accompanying file) in 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();
}
}
This is one way I've found that works...there's no doubt many other ways, and your mileage may vary...
In any event, HTH
You need to introduce ViewModels instead of shredding the WPF UI. e.g. I could create one as follows
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... }
}
Next create a list of such objects as a property in your DataContext so that your ListView can bind to it.
// e.g. MainWindow
public IEnumerable<ItemVM> Items { get; set; }
Now all you need to do is bind your ListView to this collection and wire up the DataContext of the UI properly
<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"/>
Now changing the background color is easy. Just set the property of the corresponding ItemVM object to the Color you want. e.g. to set all items to go PaleGreen
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach (var item in Items)
item.BackgroundColor = Colors.PaleGreen;
}
When using the ItemContainerGenerator
then be aware that the containers are generated asynchronously. The generator exposes a status changed event you could listen to:
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)
{
...
}
}
}
Not sure if that will create any weird drawing effects (flickering) or not.
Another option instead of building the listview items in code is to you use data templates. You might have to add a few properties to your view model for display purposes though.
After some googling i found out my own solution I am using Listview.ItemsSource and as source i use List Then i can set background of specify ListViewItem in List, and just refresh 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>
Fill ListView with Items with Gray Background:
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; }
}
Create Method for Row Change Color:
private void ChangeRowColor(int RowIndex,SolidColorBrush NewBackground)
{
ITEMS[RowIndex].Background = NewBackground;
listView.Items.Refresh();
}
And use it:
private void button1_Click(object sender, RoutedEventArgs e)
{
ChangeRowColor(4, Brushes.Green);
}
You could use the ItemContainerGenerator, e.g:
var lvitem = listView.ItemContainerGenerator.ContainerFromItem(item) as ListViewItem;
var lvitem = listView.ItemContainerGenerator.ContainerFromIndex(0) as ListViewItem;
However by default the ListView
is virtualizing, this means ListViewItems
are created on the fly as needed (only if the item is actually visible in the list), so the above methods will not return containers for items which are currently not visible.
This being the case it usually is preferable to define a binding on the Background
property via a Setter
in the ItemContainerStyle.