While there are many posts relating to doing this with a ListBox
, the ListView
control is proving quite resistant.
In a nutshell, I'd like to highlight certain rows in the ListView
as 'disabled' but still allow the user to search/see them listed.
In the commented out XAML sections, I can explicitly set the Background
for all ListViewItem
s just fine. However, if I try to bind it, it fails...though it doesn't give any warnings and runs just fine - it simply doesn't work.
I could explicitly create a TextBlock
for each column and set the Background
there but that would result in quite a bit of copypasta which I'm looking to avoid as I've quite a few columns in my real-world example.
I've tried setting a DataTemplate
but can't figure how to allow the bindings to happen per column - all the examples I've seen bind specifically to the item property (in my case, a column). I'd need such a template to set the style for all columns, but specify the column content elsewhere.
I think I'm close but I'm missing some small piece somewhere or have something not wired up correctly.
For berivity, the code to reproduce is as follows;
XAML
<Window x:Class="WpfApp1.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="200" Width="200">
<ListView Margin="5" VerticalAlignment="Stretch"
SelectionMode="Single" ItemsSource="{Binding Path=Items}"
SelectedItem="{Binding SelectedItem,Mode=TwoWay}">
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<!--<Setter Property="Background" Value="{Binding Background}" />-->
<!--<Setter Property="Background" Value="Blue" />-->
</Style>
<!--<DataTemplate DataType="{x:Type local:FooItemViewModel}">
<TextBlock Background="{Binding Background}"></TextBlock>
</DataTemplate>-->
</ListView.Resources>
<ListView.View>
<GridView AllowsColumnReorder="True">
<GridView.ColumnHeaderContainerStyle>
<Style>
<Setter Property="GridViewColumnHeader.HorizontalContentAlignment" Value="Left" />
<Setter Property="GridViewColumnHeader.Padding" Value="10 0" />
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn Header="ID" Width="Auto" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Header="Barcode" Width="Auto" DisplayMemberBinding="{Binding Barcode}" />
<GridViewColumn Header="Check" Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Selected}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Window>
Code behind for XAML
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow : Window
{
private FooViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new FooViewModel();
DataContext = _viewModel;
Loaded += MainWindow_Loaded;
}
public void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_viewModel.Items = new ObservableCollection<FooItemViewModel>
{
new FooItemViewModel { Id = 1, Barcode = "test1"},
new FooItemViewModel { Id = 2, Barcode = "test2", Selected = true},
new FooItemViewModel { Id = 3, Barcode = "test3"},
};
}
}
}
View Models
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;
namespace WpfApp1
{
internal class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void UpdateProperty<T>(ref T toSet, T value, [CallerMemberName] string propertyName = null)
{
if ((toSet == null && value != null) || !toSet.Equals(value))
{
toSet = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
internal class FooItemViewModel : BaseViewModel
{
private int _id;
private string _barcode;
private bool _selected;
public int Id
{
get => _id;
set => UpdateProperty(ref _id, value);
}
public string Barcode
{
get => _barcode;
set => UpdateProperty(ref _barcode, value);
}
public bool Selected
{
get => _selected;
set => UpdateProperty(ref _selected, value);
}
public Color Background => Color.Blue;
}
internal class FooViewModel : BaseViewModel
{
private FooItemViewModel _selectedItem;
private ObservableCollection<FooItemViewModel> _items = new ObservableCollection<FooItemViewModel>();
public FooItemViewModel Selecteditem
{
get => (FooItemViewModel)_selectedItem;
set => UpdateProperty(ref _selectedItem, value);
}
public ObservableCollection<FooItemViewModel> Items
{
get => _items;
set => UpdateProperty(ref _items, value);
}
}
}
The Background
property should should return a Brush
instead of a Color
for the binding to work:
public Brush Background => Brushes.Blue;