Good evening. There are a couple of unsolved problems in wpf, over which I have been puzzling for 3 days. I would be grateful for any help. There is the following grid:

____________________________________ |заголовок 1|заголовок 2|заголовок 3| ____________________________________ |текст 1 |текст 2 |текст 3 | ____________________________________ |красн. |желт. |красн. | ____________________________________ 

I need to color the background of the line | text 1 | text 2 | text 3 | in accordance with the values ​​in the line | red. | yellow. | red. | . A line with flowers after hide. and I need to do this without code behind. Maybe somehow you can solve this puzzle in xaml. Maybe here what converter is needed. I haven't got a solution. If there is a way, then tell me, please.

These two lines will always be in the same place and will not change. I tried to attach to the row index, but did not fully implement it.

  • And what is your type of VM element? - VladD
  • How did it happen that you in the DataGrid on different lines display elements of different types? - Andrei NOP
  • @VladD I have a binding to DataTable - Serious Down
  • @ Andrei, a little bit wrong. I just have strings in datatable. One is just the one that I need, and the other line (also the format string) is a sign of color. And depending on the value in this line, I need to color the corresponding. cell values ​​in another row. - Serious Down
  • Well, convert DataTable to a normal collection of objects, why suffer? - Andrei NOP

2 answers 2

Create a class that will describe the cell:

 class Cell { public string Value { get; } public string Color { get; } public Cell(string value, string color) { Value = value; Color = color; } } 

Create a class that will describe the string:

 class Row { public Cell Column1 { get; } public Cell Column2 { get; } public Cell Column3 { get; } public Row(Cell column1, Cell column2, Cell column3) { Column1 = column1; Column2 = column2; Column3 = column3; } } 

Get a collection of strings and write data from the DataTable into it:

 public List<Row> Table { get; } Конструктор { var values = dataTable.Rows[0].ItemArray; var colors = dataTable.Rows[1].ItemArray; Table = new List<Row>(); Table.Add ( new Row ( new Cell((string)values[0], (string)colors[0]), new Cell((string)values[1], (string)colors[1]), new Cell((string)values[2], (string)colors[2]) ) ); } 

Markup DataGrid:

 <DataGrid ItemsSource="{Binding Table}" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Header="заголовок 1" Binding="{Binding Column1.Value}"> <DataGridTextColumn.CellStyle> <Style TargetType="DataGridCell"> <Setter Property="Background" Value="{Binding Column1.Color}"/> </Style> </DataGridTextColumn.CellStyle> </DataGridTextColumn> <DataGridTextColumn Header="заголовок 2" Binding="{Binding Column2.Value}"> <DataGridTextColumn.CellStyle> <Style TargetType="DataGridCell"> <Setter Property="Background" Value="{Binding Column2.Color}"/> </Style> </DataGridTextColumn.CellStyle> </DataGridTextColumn> <DataGridTextColumn Header="заголовок 3" Binding="{Binding Column3.Value}"> <DataGridTextColumn.CellStyle> <Style TargetType="DataGridCell"> <Setter Property="Background" Value="{Binding Column3.Color}"/> </Style> </DataGridTextColumn.CellStyle> </DataGridTextColumn> </DataGrid.Columns> </DataGrid> 

Result:

Screenshot


Solution tied to DataTable (thanks for the help @VladD).

The Cell class shown above is used, we convert the input DataTable with string to Datatable with Cell :

 public DataTable Table { get; } конструктор { ... Table = new DataTable(); foreach (DataColumn column in InputTable.Columns) Table.Columns.Add(column); for (int i = 0; i < InputTable.Rows.Count / 2; ++i) { var values = InputTable.Rows[2 * i].ItemArray; var colors = InputTable.Rows[2 * i + 1].ItemArray; Table.Rows.Add(values.Zip(colors, (v, c) => new Cell((string)v, (string)c)).ToArray()); } } 

Still need a converter:

 public class DataRowViewConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is DataGridCell cell && cell.DataContext is DataRowView drv) return drv.Row[cell.Column.SortMemberPath]; else return null; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } 

Now the markup:

 <DataGrid ItemsSource="{Binding Table}" AutoGeneratingColumn="OnAutoGeneratingColumn"> <DataGrid.Resources> <local:DataRowViewConverter x:Key="drvc"/> </DataGrid.Resources> <DataGrid.CellStyle> <Style TargetType="DataGridCell"> <Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource drvc}}"/> <Setter Property="Background" Value="{Binding Tag.Color, RelativeSource={RelativeSource Self}}"/> </Style> </DataGrid.CellStyle> </DataGrid> 

and subscriber in CodeBehind:

 void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) { var column = (DataGridTextColumn)e.Column; column.Binding = new Binding { Path = new PropertyPath(nameof(FrameworkElement.Tag) + "." + nameof(Cell.Value)), RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGridCell), 1) }; column.SortMemberPath = e.PropertyName; column.IsReadOnly = true; } 

A bit complicated, but this is the DataGrid .

If you want to edit the values, then the installation of IsReadOnly needs to be removed, but do not forget then add the setter to Cell.Value

  • Thanks a lot for the example. Sorry for not writing right away: the number of columns can vary (how can you make your example more dynamic? I apologize again that I didn’t clarify this right away. The headers are taken from datatable columnname - Serious Down
  • Do you use any DataGrid functionality other than data mapping? It may be easier to drop the DataGrid benefit from the ItemsControl , for example. - Andrei NOP
  • yes i use Tracking cell changes. Some calculations in the cells. More suitable datagrid. Only here is the problem with coloring ... - Serious Down
  • In this case, take the Cell class from this response and form a new DataTable from your DataTable , which will have Cell type cells in the cells —Andrey NOP
  • @Gadman cell do you have the same type? - user227049

I suggest using the special package Gu.Wpf.DataGrid2D so that you can use binding to arrays of arrays, which is much more convenient to work with than with DataTable . To install a package using NuGet, you can use the following command

 Install-Package Gu.Wpf.DataGrid2D 

Then you can set a template for the cell in which to apply data binding. For example:

  <DataGrid Margin="5" dataGrid2D:ItemsSource.RowsSource="{Binding Data}" SelectionUnit="Cell"> <dataGrid2D:Cell.Template> <DataTemplate> <TextBox Background="{Binding Color}" Text="{Binding Value}" /> </DataTemplate> </dataGrid2D:Cell.Template> </DataGrid> 

(I assume that you use the Cell class from another answer to describe the cell).

Display example

enter image description here

As you can see, each cell can be painted in its own color.

You can set headers for rows and columns as follows:

 dataGrid2D:ItemsSource.ColumnHeadersSource="{Binding ColumnHeaders}" dataGrid2D:ItemsSource.RowHeadersSource="{Binding RowHeaders}" 

Before using it is important to remember to add an ad:

 xmlns:dataGrid2D="http://gu.se/DataGrid2D" 

or (if you have an F # project and you work through FsXAML)

 xmlns:dataGrid2D="clr-namespace:Gu.Wpf.DataGrid2D;assembly=Gu.Wpf.DataGrid2D"