I used the following links to display my 2 dimensional data in a table:
How to bind an 2D array bool[][] to a WPF DataGrid (one-way)?
Change DataGrid cell colour based on values
All is working except that the background color is not changing (and the converter method is not even being hit). Can someone tell me what's going on?
Below I post a complete, minimal example. I'm not wedded to any of these ideas (using a DataView to bind my IEnumerable> for example) so feel free to suggest alternative methods. My only hard requirement is that in my real project, the data is given as IEnumerable>
Here's the code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel vm = new ViewModel();
List<Column> row1 = new List<Column>()
{
new Column(){Make = Make.Ford,OperatingStatus = OperatingStatus.Broken},
new Column(){Make = Make.Honda, OperatingStatus = OperatingStatus.Unknown}
};
List<Column> row2 = new List<Column>()
{
new Column() {Make = Make.GM, OperatingStatus = OperatingStatus.Working},
new Column() {Make = Make.Toyota, OperatingStatus = OperatingStatus.Broken}
};
List<List<Column>> data = new List<List<Column>>();
data.Add(row1);
data.Add(row2);
vm.Data = data;
DataContext = vm;
}
}
public enum OperatingStatus
{
Working = 0,
Broken = 1,
Unknown = 2
}
public enum Make
{
Ford,
Honda,
GM,
Toyota
}
public class Column
{
public Make Make { get; set; }
public OperatingStatus OperatingStatus { get; set; }
}
public class ViewModel
{
public IEnumerable<IEnumerable<Column>> Data { get; set; }
public DataView MyDataTable
{
get
{
var rows = Data.Count();
var cols = Data.First().Count();
var t = new DataTable();
for (var c = 0; c < cols; c++)
{
t.Columns.Add(new DataColumn(c.ToString()));
}
foreach (var row in Data)
{
var newRow = t.NewRow();
int c = 0;
foreach (var col in row)
{
newRow[c] = col.Make;
c++;
}
t.Rows.Add(newRow);
}
return t.DefaultView;
}
}
}
public class NameToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string input = value as string;
switch (input)
{
case "Ford":
return Brushes.LightGreen;
case "GM":
return Brushes.Red;
case "Toyota":
return Brushes.Blue;
case "Honda":
return Brushes.Yellow;
default:
return DependencyProperty.UnsetValue;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
And here's the XAML
<Window x:Class="StackOverFlowDataGridQuestion.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:StackOverFlowDataGridQuestion"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:NameToBrushConverter x:Key="NameToBrushConverter"/>
</Window.Resources>
<Grid>
<ScrollViewer>
<DataGrid Width="1000"
Margin="0"
HorizontalAlignment="Left"
DataContext="{Binding}"
ItemsSource="{Binding MyDataTable}" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Make}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="{Binding Make, Converter={StaticResource NameToBrushConverter}}"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</Grid>
After trying Nik's suggestion and replacing 'Make' with 'Row[0]' in two places, I got the following, which is progress because there is some colour! If there is more changes, I'll report back here.
I would have expected to get something where the Ford square is green, Honda is yellow, GM is red, and Toyota is blue. Something more like below (please excuse my horrible markup skills).
Here is a solution for posterity. I don't claim it's the best or most elegant and would welcome alternative ideas. In particular, the whole idea of having to expose a DataView rather than just IEnumberable> seems crazy. In additions to the articles I initially mentioned, I also found the following extremely useful:
https://codefornothing.wordpress.com/2009/01/25/the-wpf-datagrid-and-me/ https://social.msdn.microsoft.com/Forums/vstudio/en-US/b3cbe382-99b0-4005-8cb9-cd2f36e74ed3/how-to-change-a-datagrid-cells-background-color-using-a-converter?forum=wpf
CODE
And here's the XAML
That is one of the unfortunate side effect of using a
DataView
as your ItemsSource. The DataContext of aDataGridRow
in this case is aDataRowView
which has a propertyRow
. This property contains an array of values which are the individual cells.DataGridCell
inherits thatDataContext
. Then what you're looking for isRow[0]
for the first column,Row[1]
for your second column and so on. Using the XAML below for theDataGridTextColumn
, produced the result you're looking for in my testing, where I usedRow[0]
instead ofMake
in the bindings. And thank you for providing such good working code, such a time saver!I recently needed to do something similar where my input had to be a 2-D array of indeterminate dimensions. I ended up making a reusable Custom Control extending a
DataGrid
. This control managed its own DataContext (aDataTable
), which made the UI nice a clean without the need to use indices or any code-behind.There may be a nicer way to do this, but I could not figure it out. Plus it really depends on what you're trying to achieve. If your columns are known at design time, I would consider creating an ObservableCollection containing objects. If not, maybe someone has a better trick, but at least this should get your code working.