将数据条修改为实体填充时,如何阻止其他条件格式消失?
How to stop other conditional formatting from disappearing when hackmodding databars into solid fills?
EPPlus 不支持 extLst 需要使用实体填充的 databars 条件格式。它们本身是渐变的,没有修改。
我对此进行编码以直接修改工作表的 xml(这从工作表 XML 获取数据栏,然后添加所需的 extLst 节点):
public static Random Rnd = new Random();
public static string GenerateXlsId()
{
//{29BD882A-B741-482B-9067-72CC5D939236}
string id = string.Empty;
for (int i = 0; i < 32; i++)
if (Rnd.NextDouble() < 0.5)
id += Rnd.Next(0, 10);
else
id += (char)Rnd.Next(65, 91);
id = id.Insert(8, "-");
id = id.Insert(13, "-");
id = id.Insert(18, "-");
id = id.Insert(23, "-");
return id;
}
public static void FixDatabarsAtWorksheet(OfficeOpenXml.ExcelWorksheet eworksheet)
{
System.Xml.XmlNodeList databars = eworksheet.WorksheetXml.GetElementsByTagName("dataBar");
if (databars.Count > 0)
{
string conditional_formattings_str = string.Empty;
for (int i = 0; i < databars.Count; i++)
{
string temp_databar_id = GenerateXlsId();
databars[i].ParentNode.InnerXml += @"<extLst>
<ext uri=""{B025F937-C7B1-47D3-B67F-A62EFF666E3E}"" xmlns:x14=""http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"">
<x14:id>{" + temp_databar_id + @"}</x14:id>
</ext>
</extLst>";
//--
string temp_sqref = databars[i].ParentNode.ParentNode.Attributes["sqref"].Value;
string left_type = string.Empty;
string left_val = string.Empty;
string right_type = string.Empty;
string right_val = string.Empty;
string color = string.Empty;
Color databar_fill_color = Color.Empty;
Color databar_border_color = Color.Empty;
for (int j = 0; j < databars[i].ChildNodes.Count; j++)
if (databars[i].ChildNodes[j].LocalName == "cfvo" && databars[i].ChildNodes[j].Attributes["type"] != null)
{
if (string.IsNullOrEmpty(left_type))
left_type = databars[i].ChildNodes[j].Attributes["type"].Value;
else if (string.IsNullOrEmpty(right_type))
right_type = databars[i].ChildNodes[j].Attributes["type"].Value;
if (databars[i].ChildNodes[j].Attributes["val"] != null)
if (string.IsNullOrEmpty(left_val))
left_val = databars[i].ChildNodes[j].Attributes["val"].Value;
else if (string.IsNullOrEmpty(right_val))
right_val = databars[i].ChildNodes[j].Attributes["val"].Value;
}
else if (databars[i].ChildNodes[j].LocalName == "color")
{
color = databars[i].ChildNodes[j].Attributes["rgb"].Value;
int argb = Int32.Parse(color, System.Globalization.NumberStyles.HexNumber);
databar_fill_color = Color.FromArgb(argb);
databar_border_color = Color.FromArgb(255,
databar_fill_color.R - 50 < 0 ? databar_fill_color.R + 50 : databar_fill_color.R - 50,
databar_fill_color.G - 50 < 0 ? databar_fill_color.R + 50 : databar_fill_color.G - 50,
databar_fill_color.B - 50 < 0 ? databar_fill_color.R + 50 : databar_fill_color.B - 50);
}
string temp_conditional_formatting_template = @"<x14:conditionalFormatting xmlns:xm=""http://schemas.microsoft.com/office/excel/2006/main"">
<x14:cfRule type=""dataBar"" id=""{" + temp_databar_id + @"}"">
<x14:dataBar minLength=""" + (string.IsNullOrEmpty(left_val) ? "0" : left_val) + "\" maxLength=\"" + (string.IsNullOrEmpty(right_val) ? "100" : right_val) + "\" gradient=\"0\" " + (databar_border_color.IsEmpty ? string.Empty : "border = \"1\"") + ">";
temp_conditional_formatting_template += Environment.NewLine + "<x14:cfvo type=\"" + (left_type.ToLower() == "min" ? "autoMin" : left_type) + "\" />";
temp_conditional_formatting_template += Environment.NewLine + "<x14:cfvo type=\"" + (right_type.ToLower() == "max" ? "autoMax" : right_type) + "\" />";
if (!databar_border_color.IsEmpty)
temp_conditional_formatting_template += Environment.NewLine + "<x14:borderColor rgb=\"" + BitConverter.ToString(new byte[] { databar_border_color.A, databar_border_color.R, databar_border_color.G, databar_border_color.B }).Replace("-", "") + "\" />";
temp_conditional_formatting_template += Environment.NewLine + @"</x14:dataBar>
</x14:cfRule>
<xm:sqref>" + temp_sqref + @"</xm:sqref>
</x14:conditionalFormatting>";
conditional_formattings_str += temp_conditional_formatting_template;
}
databars[0].ParentNode.ParentNode.ParentNode.InnerXml += @"<extLst>
<ext uri=""{78C0D931-6437-407d-A8EE-F0AAD7539E65}"" xmlns:x14=""http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"">
<x14:conditionalFormattings>" + conditional_formattings_str + @"
</x14:conditionalFormattings>
</ext>
</extLst>";
}
}
这确实使数据栏实心填充,问题是任何其他条件格式(如 GreaterThan)在为真时都会失去其样式。
例如,我添加了数据栏和 GreaterThan 123(绿色)条件格式。
Excel 仍然看到条件格式规则大于 123,但当它为真时未设置样式(未设置绿色)。同时正确显示数据条。
我不知道去哪里看...有人帮忙!
这就是黑客的问题 - 它们太脆弱了! :)
我能够通过另一个 hack 使其工作 - 设置样式差异格式 (dxf) 参考,它似乎在 epplus 保存时被删除。可能发生的情况是 epplus 只认为保存时有一个 dxf,所以它没有设置值,因为 excel 将假定它是第一个 dxf 样式(索引 0),但这有点猜测。
无论如何,如果您通过 XML 手动设置 dxfid
,它会找到它。但是这里的顺序很重要,你必须最后应用数据栏黑客,否则它会命中错误的参考:
[TestMethod]
public void FixDatabarsAtWorksheetTest()
{
//
//Throw in some data
var datatable = new DataTable("tblData");
datatable.Columns.AddRange(new[]
{
new DataColumn("Col1", typeof(int)), new DataColumn("Col2", typeof(int)), new DataColumn("Col3", typeof(object))
});
for (var i = 0; i < 10; i++)
{
var row = datatable.NewRow();
row[0] = i;
row[1] = i * 10;
row[2] = Path.GetRandomFileName();
datatable.Rows.Add(row);
}
//Create a test file
var fi = new FileInfo(@"c:\temp\FixDatabarsAtWorksheetTest.xlsx");
if (fi.Exists)
fi.Delete();
using (var pck = new ExcelPackage(fi))
{
var workbook = pck.Workbook;
var doc = workbook.Worksheets.Add("Sheet1");
doc.Cells.LoadFromDataTable(datatable, true);
//Set the greater than
var gtConditional = doc
.ConditionalFormatting
.AddGreaterThan(doc.Cells["A2:A11"]);
gtConditional.Formula = "2";
gtConditional.Style.Fill.BackgroundColor.Color = Color.GreenYellow;
//Fix the gt
var xdoc = doc.WorksheetXml;
var nsm = new XmlNamespaceManager(xdoc.NameTable);
nsm.AddNamespace("default", xdoc.DocumentElement.NamespaceURI);
var gtNode = xdoc.SelectSingleNode("/default:worksheet/default:conditionalFormatting[@sqref=\"A2:A11\"]", nsm);
//Create the new attribute for table
var att = xdoc.CreateAttribute("dxfId");
att.Value = "0";
gtNode
.FirstChild
.Attributes.Append(att);
//Set the bar condition LAST
var barConditional = doc
.ConditionalFormatting
.AddDatabar(doc.Cells["B2:B11"], Color.FromArgb(99, 195, 132));
barConditional.HighValue.Type = eExcelConditionalFormattingValueObjectType.Num;
barConditional.LowValue.Type = eExcelConditionalFormattingValueObjectType.Num;
barConditional.HighValue.Value = 82;
barConditional.LowValue.Value = 0;
FixDatabarsAtWorksheet(doc);
pck.Save();
}
}
我明白了:
不确定这对你有多可行,具体取决于你有多少条件格式,但值得一试。
EPPlus 不支持 extLst 需要使用实体填充的 databars 条件格式。它们本身是渐变的,没有修改。
我对此进行编码以直接修改工作表的 xml(这从工作表 XML 获取数据栏,然后添加所需的 extLst 节点):
public static Random Rnd = new Random();
public static string GenerateXlsId()
{
//{29BD882A-B741-482B-9067-72CC5D939236}
string id = string.Empty;
for (int i = 0; i < 32; i++)
if (Rnd.NextDouble() < 0.5)
id += Rnd.Next(0, 10);
else
id += (char)Rnd.Next(65, 91);
id = id.Insert(8, "-");
id = id.Insert(13, "-");
id = id.Insert(18, "-");
id = id.Insert(23, "-");
return id;
}
public static void FixDatabarsAtWorksheet(OfficeOpenXml.ExcelWorksheet eworksheet)
{
System.Xml.XmlNodeList databars = eworksheet.WorksheetXml.GetElementsByTagName("dataBar");
if (databars.Count > 0)
{
string conditional_formattings_str = string.Empty;
for (int i = 0; i < databars.Count; i++)
{
string temp_databar_id = GenerateXlsId();
databars[i].ParentNode.InnerXml += @"<extLst>
<ext uri=""{B025F937-C7B1-47D3-B67F-A62EFF666E3E}"" xmlns:x14=""http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"">
<x14:id>{" + temp_databar_id + @"}</x14:id>
</ext>
</extLst>";
//--
string temp_sqref = databars[i].ParentNode.ParentNode.Attributes["sqref"].Value;
string left_type = string.Empty;
string left_val = string.Empty;
string right_type = string.Empty;
string right_val = string.Empty;
string color = string.Empty;
Color databar_fill_color = Color.Empty;
Color databar_border_color = Color.Empty;
for (int j = 0; j < databars[i].ChildNodes.Count; j++)
if (databars[i].ChildNodes[j].LocalName == "cfvo" && databars[i].ChildNodes[j].Attributes["type"] != null)
{
if (string.IsNullOrEmpty(left_type))
left_type = databars[i].ChildNodes[j].Attributes["type"].Value;
else if (string.IsNullOrEmpty(right_type))
right_type = databars[i].ChildNodes[j].Attributes["type"].Value;
if (databars[i].ChildNodes[j].Attributes["val"] != null)
if (string.IsNullOrEmpty(left_val))
left_val = databars[i].ChildNodes[j].Attributes["val"].Value;
else if (string.IsNullOrEmpty(right_val))
right_val = databars[i].ChildNodes[j].Attributes["val"].Value;
}
else if (databars[i].ChildNodes[j].LocalName == "color")
{
color = databars[i].ChildNodes[j].Attributes["rgb"].Value;
int argb = Int32.Parse(color, System.Globalization.NumberStyles.HexNumber);
databar_fill_color = Color.FromArgb(argb);
databar_border_color = Color.FromArgb(255,
databar_fill_color.R - 50 < 0 ? databar_fill_color.R + 50 : databar_fill_color.R - 50,
databar_fill_color.G - 50 < 0 ? databar_fill_color.R + 50 : databar_fill_color.G - 50,
databar_fill_color.B - 50 < 0 ? databar_fill_color.R + 50 : databar_fill_color.B - 50);
}
string temp_conditional_formatting_template = @"<x14:conditionalFormatting xmlns:xm=""http://schemas.microsoft.com/office/excel/2006/main"">
<x14:cfRule type=""dataBar"" id=""{" + temp_databar_id + @"}"">
<x14:dataBar minLength=""" + (string.IsNullOrEmpty(left_val) ? "0" : left_val) + "\" maxLength=\"" + (string.IsNullOrEmpty(right_val) ? "100" : right_val) + "\" gradient=\"0\" " + (databar_border_color.IsEmpty ? string.Empty : "border = \"1\"") + ">";
temp_conditional_formatting_template += Environment.NewLine + "<x14:cfvo type=\"" + (left_type.ToLower() == "min" ? "autoMin" : left_type) + "\" />";
temp_conditional_formatting_template += Environment.NewLine + "<x14:cfvo type=\"" + (right_type.ToLower() == "max" ? "autoMax" : right_type) + "\" />";
if (!databar_border_color.IsEmpty)
temp_conditional_formatting_template += Environment.NewLine + "<x14:borderColor rgb=\"" + BitConverter.ToString(new byte[] { databar_border_color.A, databar_border_color.R, databar_border_color.G, databar_border_color.B }).Replace("-", "") + "\" />";
temp_conditional_formatting_template += Environment.NewLine + @"</x14:dataBar>
</x14:cfRule>
<xm:sqref>" + temp_sqref + @"</xm:sqref>
</x14:conditionalFormatting>";
conditional_formattings_str += temp_conditional_formatting_template;
}
databars[0].ParentNode.ParentNode.ParentNode.InnerXml += @"<extLst>
<ext uri=""{78C0D931-6437-407d-A8EE-F0AAD7539E65}"" xmlns:x14=""http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"">
<x14:conditionalFormattings>" + conditional_formattings_str + @"
</x14:conditionalFormattings>
</ext>
</extLst>";
}
}
这确实使数据栏实心填充,问题是任何其他条件格式(如 GreaterThan)在为真时都会失去其样式。
例如,我添加了数据栏和 GreaterThan 123(绿色)条件格式。 Excel 仍然看到条件格式规则大于 123,但当它为真时未设置样式(未设置绿色)。同时正确显示数据条。
我不知道去哪里看...有人帮忙!
这就是黑客的问题 - 它们太脆弱了! :)
我能够通过另一个 hack 使其工作 - 设置样式差异格式 (dxf) 参考,它似乎在 epplus 保存时被删除。可能发生的情况是 epplus 只认为保存时有一个 dxf,所以它没有设置值,因为 excel 将假定它是第一个 dxf 样式(索引 0),但这有点猜测。
无论如何,如果您通过 XML 手动设置 dxfid
,它会找到它。但是这里的顺序很重要,你必须最后应用数据栏黑客,否则它会命中错误的参考:
[TestMethod]
public void FixDatabarsAtWorksheetTest()
{
//
//Throw in some data
var datatable = new DataTable("tblData");
datatable.Columns.AddRange(new[]
{
new DataColumn("Col1", typeof(int)), new DataColumn("Col2", typeof(int)), new DataColumn("Col3", typeof(object))
});
for (var i = 0; i < 10; i++)
{
var row = datatable.NewRow();
row[0] = i;
row[1] = i * 10;
row[2] = Path.GetRandomFileName();
datatable.Rows.Add(row);
}
//Create a test file
var fi = new FileInfo(@"c:\temp\FixDatabarsAtWorksheetTest.xlsx");
if (fi.Exists)
fi.Delete();
using (var pck = new ExcelPackage(fi))
{
var workbook = pck.Workbook;
var doc = workbook.Worksheets.Add("Sheet1");
doc.Cells.LoadFromDataTable(datatable, true);
//Set the greater than
var gtConditional = doc
.ConditionalFormatting
.AddGreaterThan(doc.Cells["A2:A11"]);
gtConditional.Formula = "2";
gtConditional.Style.Fill.BackgroundColor.Color = Color.GreenYellow;
//Fix the gt
var xdoc = doc.WorksheetXml;
var nsm = new XmlNamespaceManager(xdoc.NameTable);
nsm.AddNamespace("default", xdoc.DocumentElement.NamespaceURI);
var gtNode = xdoc.SelectSingleNode("/default:worksheet/default:conditionalFormatting[@sqref=\"A2:A11\"]", nsm);
//Create the new attribute for table
var att = xdoc.CreateAttribute("dxfId");
att.Value = "0";
gtNode
.FirstChild
.Attributes.Append(att);
//Set the bar condition LAST
var barConditional = doc
.ConditionalFormatting
.AddDatabar(doc.Cells["B2:B11"], Color.FromArgb(99, 195, 132));
barConditional.HighValue.Type = eExcelConditionalFormattingValueObjectType.Num;
barConditional.LowValue.Type = eExcelConditionalFormattingValueObjectType.Num;
barConditional.HighValue.Value = 82;
barConditional.LowValue.Value = 0;
FixDatabarsAtWorksheet(doc);
pck.Save();
}
}
我明白了:
不确定这对你有多可行,具体取决于你有多少条件格式,但值得一试。