After your comment to my previous answer, I don't think this is possible using a colorScale
as you'd either need two scales or one with four colors (neither of which is allowed). You could create your own though by using conditional formats with formulae.
Using this approach you can make it work without the need for VBA and any sorting or editing of the sheet will still work.
I've knocked up a (very) rough example together that shows how this could work. It's a bit rough in as much as it will create a new conditional format for every value; it would be neater to create one per range that you're interested in (maybe using percentiles) but it's a starting point.
The bulk of the work is done in the following two methods. I've added some summary comments to them, if they need more explanation just let me know.
/// <summary>
/// Adds a conditional format to the sheet based on the value passed in
/// </summary>
/// <param name="value">The value going into the cell</param>
/// <param name="minValue">The minimum value in the whole range of values going into the sheet</param>
/// <param name="maxValue">The maximum value in the whole range of values going into the sheet</param>
/// <param name="ignoreRangeLowValue">The lowest value in the mid-point. A value greater than or equal to this and less than or equal to the ignoreRangeHighValue will be unstyled</param>
/// <param name="ignoreRangeHighValue">The highest value in the mid-point. A value greater than or equal to the ignoreRangeLowValue and less than or equal to this value will be unstyled</param>
/// <param name="lowValuesMinColor">The colour of the lowest value below the mid-point</param>
/// <param name="lowValuesMaxColor">The colour of the highest value below the mid-point</param>
/// <param name="highValuesMinColor">The colour of the lowest value above the mid-point</param>
/// <param name="highValuesMaxColor">The colour of the highest value above the mid-point</param>
/// <param name="differentialFormats">A DifferentialFormats object to add the formats to</param>
/// <param name="conditionalFormatting">A ConditionalFormatting object to add the conditional formats to</param>
private static void AddConditionalStyle(decimal value,
decimal minValue,
decimal maxValue,
decimal ignoreRangeLowValue,
decimal ignoreRangeHighValue,
System.Drawing.Color lowValuesMinColor,
System.Drawing.Color lowValuesMaxColor,
System.Drawing.Color highValuesMinColor,
System.Drawing.Color highValuesMaxColor,
DifferentialFormats differentialFormats,
ConditionalFormatting conditionalFormatting)
{
System.Drawing.Color fillColor;
if (value >= ignoreRangeLowValue && value <= ignoreRangeHighValue)
return;
if (value > ignoreRangeHighValue)
{
fillColor = GetColour(value, ignoreRangeHighValue, maxValue, highValuesMinColor, highValuesMaxColor);
}
else
{
fillColor = GetColour(value, minValue, ignoreRangeLowValue, lowValuesMinColor, lowValuesMaxColor);
}
DifferentialFormat differentialFormat = new DifferentialFormat();
Fill fill = new Fill();
PatternFill patternFill = new PatternFill();
BackgroundColor backgroundColor = new BackgroundColor() { Rgb = fillColor.Name };
patternFill.Append(backgroundColor);
fill.Append(patternFill);
differentialFormat.Append(fill);
differentialFormats.Append(differentialFormat);
ConditionalFormattingOperatorValues op = ConditionalFormattingOperatorValues.Between;
Formula formula1 = null;
Formula formula2 = null;
if (value > maxValue)
{
op = ConditionalFormattingOperatorValues.GreaterThanOrEqual;
formula1 = new Formula();
formula1.Text = value.ToString();
}
else if (value < minValue)
{
op = ConditionalFormattingOperatorValues.LessThanOrEqual;
formula1 = new Formula();
formula1.Text = value.ToString();
}
else
{
formula1 = new Formula();
formula1.Text = (value - 0.05M).ToString();
formula2 = new Formula();
formula2.Text = (value + 0.05M).ToString();
}
ConditionalFormattingRule conditionalFormattingRule = new ConditionalFormattingRule()
{
Type = ConditionalFormatValues.CellIs,
FormatId = (UInt32Value)formatId++,
Priority = 1,
Operator = op
};
if (formula1 != null)
conditionalFormattingRule.Append(formula1);
if (formula2 != null)
conditionalFormattingRule.Append(formula2);
conditionalFormatting.Append(conditionalFormattingRule);
}
/// <summary>
/// Returns a Color based on a linear gradient
/// </summary>
/// <param name="value">The value being output in the cell</param>
/// <param name="minValue">The minimum value in the whole range of values going into the sheet</param>
/// <param name="maxValue">The maximum value in the whole range of values going into the sheet</param>
/// <param name="minColor">The color of the low end of the scale</param>
/// <param name="maxColor">The color of the high end of the scale</param>
/// <returns></returns>
private static System.Drawing.Color GetColour(decimal value,
decimal minValue,
decimal maxValue,
System.Drawing.Color minColor,
System.Drawing.Color maxColor)
{
System.Drawing.Color val;
if (value < minValue)
val = minColor;
else if (value > maxValue)
val = maxColor;
else
{
decimal scaleValue = (value - minValue) / (maxValue - minValue);
int r = (int)(minColor.R + ((maxColor.R - minColor.R) * scaleValue));
int g = (int)(minColor.G + ((maxColor.G - minColor.G) * scaleValue));
int b = (int)(minColor.B + ((maxColor.B - minColor.B) * scaleValue));
val = System.Drawing.Color.FromArgb(r, g, b);
}
return val;
}
As an example usage I've created this:
static uint formatId = 0U;
public static void CreateSpreadsheetWorkbook(string filepath)
{
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.
Create(filepath, SpreadsheetDocumentType.Workbook);
// Add a WorkbookPart to the document.
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
// Add a WorksheetPart to the WorkbookPart.
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
SheetData sheetData = new SheetData();
worksheetPart.Worksheet = new Worksheet(sheetData);
// Add Sheets to the Workbook.
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.
AppendChild<Sheets>(new Sheets());
// Append a new worksheet and associate it with the workbook.
Sheet sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "FormattedSheet"
};
sheets.Append(sheet);
WorkbookStylesPart stylesPart = workbookpart.AddNewPart<WorkbookStylesPart>();
stylesPart.Stylesheet = new Stylesheet();
Fills fills = new Fills() { Count = (UInt32Value)20U }; //this count is slightly out; we should calculate it really
//this could probably be more efficient - we don't really need one for each value; we could put them in percentiles for example
DifferentialFormats differentialFormats = new DifferentialFormats() { Count = (UInt32Value)20U };
ConditionalFormatting conditionalFormatting = new ConditionalFormatting() { SequenceOfReferences = new ListValue<StringValue>() { InnerText = "A1:A21" } };
for (decimal i = 1; i > -1.1M; i -= 0.1M)
{
AddConditionalStyle(i, -0.8M, 0.8M, 0M, 0M,
System.Drawing.Color.FromArgb(152, 83, 84),
System.Drawing.Color.FromArgb(244, 221, 221),
System.Drawing.Color.FromArgb(214, 244, 214),
System.Drawing.Color.FromArgb(20, 134, 33),
differentialFormats,
conditionalFormatting);
}
worksheetPart.Worksheet.Append(conditionalFormatting);
stylesPart.Stylesheet.Append(differentialFormats);
uint rowId = 1U;
for (decimal i = 1; i > -1.1M; i -= 0.1M)
{
Cell cell = new Cell();
cell.DataType = CellValues.Number;
cell.CellValue = new CellValue(i.ToString());
Row row = new Row() { RowIndex = rowId++ };
row.Append(cell);
sheetData.Append(row);
}
workbookpart.Workbook.Save();
spreadsheetDocument.Close();
}
Which creates a spreadsheet with that looks like this:
