问题
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).
回答1:
That is one of the unfortunate side effect of using a DataView
as your ItemsSource. The DataContext of a DataGridRow
in this case is a DataRowView
which has a property Row
. This property contains an array of values which are the individual cells. DataGridCell
inherits that DataContext
. Then what you're looking for is Row[0]
for the first column, Row[1]
for your second column and so on. Using the XAML below for the DataGridTextColumn
, produced the result you're looking for in my testing, where I used Row[0]
instead of Make
in the bindings. And thank you for providing such good working code, such a time saver!
<DataGridTextColumn Binding="{Binding Row[0]}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="{Binding Row[0], Converter={StaticResource NameToBrushConverter}}"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
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 (a DataTable
), 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.
回答2:
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
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()));
//t.Columns.Add(new DataColumn(c.ToString(),typeof(StackOverFlowDataGridQuestion.Column)));
}
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 ConverterHoldoffGridColor : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[1] is DataRow)
{
var cell = (DataGridCell) values[0];
var row = (DataRow) values[1];
var columnName = cell.Column.SortMemberPath;
string input = (row[columnName] as string);
//string input = (row[columnName] as StackOverFlowDataGridQuestion.Column).Make.ToString();
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;
}
}
else
{
return SystemColors.AppWorkspaceColor;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
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:ConverterHoldoffGridColor x:Key="bgHoldoffGridColor" />
<Style x:Key="CellHighlighterStyle">
<Setter Property="DataGridCell.Background">
<Setter.Value>
<MultiBinding
Converter="{StaticResource bgHoldoffGridColor}" >
<MultiBinding.Bindings>
<Binding RelativeSource="{RelativeSource Self}"/>
<Binding Path="Row" Mode="OneWay"/>
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ScrollViewer>
<DataGrid x:Name="myDataGrid" CellStyle="{StaticResource CellHighlighterStyle}"
DataContext="{Binding }"
ItemsSource="{Binding MyDataTable}">
</DataGrid>
</ScrollViewer>
</Grid>
来源:https://stackoverflow.com/questions/49868151/changing-background-color-of-cells-in-datagrid-wpf