I have a typed dataset that is being bound to a datagrid (MVVM). I also have a list of Points (X and Y in the typed dataset) that say which cells have errors. The logic to detect this is complex and is run on the server end.
My goal is to paint each cell's background a different colour if they have an error, (i.e. the list of points contains that Cell's X and Y).
The dataGrid is defined as:
<DataGrid x:Name="gridData" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
BorderThickness="0 0 0 0" Margin="0 0 0 0" AutoGenerateColumns="True" AlternationCount="1" AlternatingRowBackground="AliceBlue"
ItemsSource="{Binding Path=EquisDataTable}" SelectionUnit="Cell" SelectedCellsChanged="gridData_SelectedCellsChanged"
AutoGeneratedColumns="GridData_OnAutoGeneratedColumns" AutoGeneratingColumn="gridData_AutoGeneratingColumn" Height="350">
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Setters>
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource onErrorConverter}">
<Binding RelativeSource="{RelativeSource Self}" />
<Binding RelativeSource="{RelativeSource AncestorType=SampleTests:SampleTestUserControlBase}" Path="DataContext.Problems" />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</DataGrid.CellStyle>
</DataGrid>
I believe I am close to it now (taken a lot of testing and Googling to get here). I can find a Cell's X and Y when a cell selection is changed after everything has been populated, "SelectedCellsChanged" method. This does not let me get to every cell and check its value though on load.
private void gridData_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
int x;
int y;
if (TryGetDataGridCellXandY(gridData.CurrentCell, gridData, out x, out y))
{ }
}
private bool TryGetDataGridCellXandY(DataGridCellInfo dgc, DataGrid dataGrid, out int x, out int y)
{
int columnIndex = dgc.Column.DisplayIndex;
return TryGetDataGridCellXandY(columnIndex, dataGrid, out x, out y);
}
private bool TryGetDataGridCellXandY(int columnIndex, DataGrid dataGrid, out int x, out int y)
{
DataGridCellInfo currentCell = dataGrid.CurrentCell;
int rowIndex = int.MinValue;
DataRowView rowView = currentCell.Item as DataRowView;
if (rowView != null)
{
DataRow dataRow = rowView.Row;
FieldInfo fi = typeof(DataRow).GetField("_rowID", BindingFlags.NonPublic | BindingFlags.Instance);
try
{
if (fi != null)
{
rowIndex = System.Convert.ToInt32(fi.GetValue(dataRow));
}
}
catch (InvalidCastException) { }
}
x = columnIndex;
y = rowIndex;
return x > 0 && y > 0;
}
So I created a multi value converter to do the same thing. The problem arises when a Cell's Column is null when using the converter, also the currentCell.Item (DataGridCellInfo).Item will have {DependencyProperty.UnsetValue}.
public class DataGridCellOnErrorConversion : IMultiValueConverter
{
private const string DefaultColour = "White";
private const string ErrorColour = "Red";
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 3)
return DefaultColour;
DataGridCell dgc = values[0] as DataGridCell;
if(dgc == null)
return DefaultColour;
IList<Point> problems = values[1开发者_JAVA百科] as IList<Point>;
if(problems == null)
return DefaultColour;
DataGrid grid = values[2] as DataGrid;
if (grid == null)
return DefaultColour;
int x;
int y;
if (TryGetDataGridCellXandY(grid.CurrentCell, grid, out x, out y))
{
if (problems.Any(problem => System.Convert.ToInt32(problem.X) == x && System.Convert.ToInt32(problem.Y) == y))
{
return ErrorColour;
}
}
return DefaultColour;
}
private bool TryGetDataGridCellXandY(DataGridCellInfo dgc, DataGrid dataGrid, out int x, out int y)
{
int columnIndex = dgc.Column.DisplayIndex;
return TryGetDataGridCellXandY(columnIndex, dataGrid, out x, out y);
}
private bool TryGetDataGridCellXandY(int columnIndex, DataGrid dataGrid, out int x, out int y)
{
DataGridCellInfo currentCell = dataGrid.CurrentCell;
int rowIndex = int.MinValue;
DataRowView rowView = currentCell.Item as DataRowView;
if (rowView != null)
{
DataRow dataRow = rowView.Row;
FieldInfo fi = typeof(DataRow).GetField("_rowID", BindingFlags.NonPublic | BindingFlags.Instance);
try
{
if (fi != null)
{
rowIndex = System.Convert.ToInt32(fi.GetValue(dataRow));
}
}
catch (InvalidCastException) { }
}
x = columnIndex;
y = rowIndex;
return x > 0 && y > 0;
}
}
Is this because of a binding / creation order? Is there a way around this?
Thanks
This is how I solved my problem, (not sure if it is optimal but seems to work, (so far):
I have a multi value converter on the Style for the datagrid cell:
<Style TargetType="{x:Type DataGridCell}">
<Style.Setters>
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource onErrorConverter}">
<Binding RelativeSource="{RelativeSource Self}" />
<Binding RelativeSource="{RelativeSource AncestorType=SampleTests:SampleTestUserControlBase}" Path="DataContext.Problems" />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
And the converter:
public class DataGridCellOnErrorConversion : IMultiValueConverter
{
private readonly SolidColorBrush DefaultColour = new SolidColorBrush(Colors.White);
private readonly SolidColorBrush ErrorColour = new SolidColorBrush(Colors.Red);
private readonly SolidColorBrush AlternatingColour = new SolidColorBrush(Colors.AliceBlue);
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 3)
return DefaultColour;
DataGridCell dgc = values[0] as DataGridCell;
if(dgc == null)
return DefaultColour;
IList<Point> problems = values[1] as IList<Point>;
if(problems == null)
return DefaultColour;
DataGrid grid = values[2] as DataGrid;
if (grid == null)
return DefaultColour;
int x;
int y = -1;
ItemCollection itemCollection = grid.Items;
for (int i = 0; i < itemCollection.Count; i++)
{
if (itemCollection.CurrentItem == itemCollection[i])
y = i;
}
x = dgc.Column.DisplayIndex;
DataRowView currentRowView = null;
FieldInfo fi = dgc.GetType().GetField("_owner", BindingFlags.NonPublic | BindingFlags.Instance);
try
{
if (fi != null)
{
DataGridRow dataGridRow = fi.GetValue(dgc) as DataGridRow;
if(dataGridRow != null)
currentRowView = dataGridRow.Item as DataRowView;
}
}
catch (InvalidCastException) { }
if(currentRowView != null)
{
for (int i = 0; i < itemCollection.Count; i++)
{
if (currentRowView == itemCollection[i])
y = i;
}
}
if (problems.Any(problem => System.Convert.ToInt32(problem.X) == x && System.Convert.ToInt32(problem.Y) == y))
{
return ErrorColour;
}
return y % 2 == 0 ? AlternatingColour : DefaultColour;
}
}
精彩评论