Excel >255 个字符的彩色文本部分
Color text parts in Excel >255 characters
我为第三方 sw 编写了一个插件,可以将修改后的文本提取到 Excel sheet,然后在 Excel 中为更改的部分着色。
只要每个文本段(= 单元格内容)不超过 255 个字符,此方法就有效。唉,这偶尔会发生。
为了识别 Excel 中更改的部分,我分别用 <del>
包围它们。 <add>
个用于删除和添加文本的标签。然后我像这样给这些部分上色(并删除周围的标签):
while (((string)cell1.Text).Contains("<del>"))
{
try
{
var pos = ((string) cell1.Text).IndexOf("<del>") + 1;
var pos2 = ((string) cell1.Text).IndexOf("</del>") + 1;
var txt = cell1.Characters[pos, (pos2-pos) + 9].Text;
txt = txt.Replace("<del>", "").Replace("</del>", "");
cell1.Characters[pos, (pos2-pos) + 9].Text = txt;
cell1.Characters[pos, txt.Length-3].Font.Color = -16776961;
}
catch
{
break;
}
}
我正在使用 Interop,因为我发现它更易于使用,还因为我找不到任何关于如何使用 OpenXML 执行此操作的合适示例。但是我知道 Excel 在单元格文本方面有其局限性,因此我愿意接受建议。
有没有办法使用 Interop 为包含 >255 个字符的单元格中的单个单词着色?
如果一切都失败了,我可能不得不创建一个带有 table 的 Word 文档,在那里进行格式化,然后 copy/paste 到 Excel(yukk)。请帮助我避免这种丑陋。
P.S: 是的,修订摘要需要以Excel为基础。
好的,我现在用OpenXML解决了。
如果一个单元格包含要着色的文本,我创建一个 运行 到该位置的文本,第二个包含受影响文本的彩色 运行,第三个返回默认值 运行 包含其余部分。
var xlsx = SpreadsheetDocument.Open(xlsPath, true);
var contents = xlsx.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
foreach (SharedStringItem si in contents.SharedStringTable.Elements<SharedStringItem>())
{
if (si.Text != null)
{
XlHelper.ColorCellText(si, "del", new DocumentFormat.OpenXml.Spreadsheet.Color { Rgb = "FFFF0000" });
XlHelper.ColorCellText(si, "add", new DocumentFormat.OpenXml.Spreadsheet.Color { Rgb = "0000BF00" });
}
}
还有我的XlHelper.ColorCellText方法:
public static void ColorCellText(SharedStringItem si, string TagName, DocumentFormat.OpenXml.Spreadsheet.Color col)
{
var newText = si.InnerText;
var startTag = string.Format("<{0}>", TagName);
var endTag = string.Format("</{0}>", TagName);
if (newText.Contains(startTag))
{
si.Text.Remove();
var lastpos = 0;
while (newText.Contains(startTag))
{
try
{
var pos1 = newText.IndexOf(startTag);
var pos2 = newText.IndexOf(endTag);
var txtLen = pos2 - pos1 - 5;
var it = string.Concat(newText.Substring(0, pos1), newText.Substring(pos1 + 5, txtLen),
newText.Substring(pos2 + 6));
var run = new Run();
var txt = new Text
{
Text = it.Substring(0, pos1),
Space = SpaceProcessingModeValues.Preserve
};
run.Append(txt);
si.Append(run);
run = new Run();
txt = new Text
{
Text = it.Substring(pos1, txtLen),
Space = SpaceProcessingModeValues.Preserve
};
var rp = new RunProperties();
rp.Append(col.CloneNode(true));
run.RunProperties = rp;
run.Append(txt.CloneNode(true));
si.Append(run.CloneNode(true));
newText = newText.Substring(pos2 + 6);
}
catch(Exception ex)
{
using (var sw = new StreamWriter(logFile, true))
{
sw.WriteLine("Error: {0}\r\n{1}", ex.Message, newText);
}
break;
}
}
if (newText.Length>=0)
{
var lastrun = new Run();
var lasttxt = new Text
{
Text = newText,
Space = SpaceProcessingModeValues.Preserve
};
lastrun.Append(lasttxt);
si.Append(lastrun);
}
}
}
Space = SpaceProcessingModeValues.Preserve
部分在这里至关重要,否则它将把所有三个部分粘合在一起并消除它们之间的空格。
我想我会研究一下这个 EPPlus,因为它的 "In-cell Richtext" 功能听起来很有前途。
我为第三方 sw 编写了一个插件,可以将修改后的文本提取到 Excel sheet,然后在 Excel 中为更改的部分着色。 只要每个文本段(= 单元格内容)不超过 255 个字符,此方法就有效。唉,这偶尔会发生。
为了识别 Excel 中更改的部分,我分别用 <del>
包围它们。 <add>
个用于删除和添加文本的标签。然后我像这样给这些部分上色(并删除周围的标签):
while (((string)cell1.Text).Contains("<del>"))
{
try
{
var pos = ((string) cell1.Text).IndexOf("<del>") + 1;
var pos2 = ((string) cell1.Text).IndexOf("</del>") + 1;
var txt = cell1.Characters[pos, (pos2-pos) + 9].Text;
txt = txt.Replace("<del>", "").Replace("</del>", "");
cell1.Characters[pos, (pos2-pos) + 9].Text = txt;
cell1.Characters[pos, txt.Length-3].Font.Color = -16776961;
}
catch
{
break;
}
}
我正在使用 Interop,因为我发现它更易于使用,还因为我找不到任何关于如何使用 OpenXML 执行此操作的合适示例。但是我知道 Excel 在单元格文本方面有其局限性,因此我愿意接受建议。
有没有办法使用 Interop 为包含 >255 个字符的单元格中的单个单词着色?
如果一切都失败了,我可能不得不创建一个带有 table 的 Word 文档,在那里进行格式化,然后 copy/paste 到 Excel(yukk)。请帮助我避免这种丑陋。
P.S: 是的,修订摘要需要以Excel为基础。
好的,我现在用OpenXML解决了。 如果一个单元格包含要着色的文本,我创建一个 运行 到该位置的文本,第二个包含受影响文本的彩色 运行,第三个返回默认值 运行 包含其余部分。
var xlsx = SpreadsheetDocument.Open(xlsPath, true);
var contents = xlsx.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
foreach (SharedStringItem si in contents.SharedStringTable.Elements<SharedStringItem>())
{
if (si.Text != null)
{
XlHelper.ColorCellText(si, "del", new DocumentFormat.OpenXml.Spreadsheet.Color { Rgb = "FFFF0000" });
XlHelper.ColorCellText(si, "add", new DocumentFormat.OpenXml.Spreadsheet.Color { Rgb = "0000BF00" });
}
}
还有我的XlHelper.ColorCellText方法:
public static void ColorCellText(SharedStringItem si, string TagName, DocumentFormat.OpenXml.Spreadsheet.Color col)
{
var newText = si.InnerText;
var startTag = string.Format("<{0}>", TagName);
var endTag = string.Format("</{0}>", TagName);
if (newText.Contains(startTag))
{
si.Text.Remove();
var lastpos = 0;
while (newText.Contains(startTag))
{
try
{
var pos1 = newText.IndexOf(startTag);
var pos2 = newText.IndexOf(endTag);
var txtLen = pos2 - pos1 - 5;
var it = string.Concat(newText.Substring(0, pos1), newText.Substring(pos1 + 5, txtLen),
newText.Substring(pos2 + 6));
var run = new Run();
var txt = new Text
{
Text = it.Substring(0, pos1),
Space = SpaceProcessingModeValues.Preserve
};
run.Append(txt);
si.Append(run);
run = new Run();
txt = new Text
{
Text = it.Substring(pos1, txtLen),
Space = SpaceProcessingModeValues.Preserve
};
var rp = new RunProperties();
rp.Append(col.CloneNode(true));
run.RunProperties = rp;
run.Append(txt.CloneNode(true));
si.Append(run.CloneNode(true));
newText = newText.Substring(pos2 + 6);
}
catch(Exception ex)
{
using (var sw = new StreamWriter(logFile, true))
{
sw.WriteLine("Error: {0}\r\n{1}", ex.Message, newText);
}
break;
}
}
if (newText.Length>=0)
{
var lastrun = new Run();
var lasttxt = new Text
{
Text = newText,
Space = SpaceProcessingModeValues.Preserve
};
lastrun.Append(lasttxt);
si.Append(lastrun);
}
}
}
Space = SpaceProcessingModeValues.Preserve
部分在这里至关重要,否则它将把所有三个部分粘合在一起并消除它们之间的空格。
我想我会研究一下这个 EPPlus,因为它的 "In-cell Richtext" 功能听起来很有前途。