You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何结合Excel Interop与ClosedXml实现单元格样式还原?

解决ClosedXml与Excel Interop格式枚举不兼容的问题

我之前在做类似的VSTO插件时也踩过这个坑——ClosedXml和Excel Interop的格式枚举体系完全是两套,直接赋值肯定会报0x800A03EC错误(本质是枚举类型不匹配)。咱们一步步拆解问题,给出可行的解决方案:

错误根源分析

你代码里的两个报错点,本质都是属性对应错误或者枚举值不匹配

  1. 填充Pattern报错:你把ClosedXml的PatternColor(图案颜色)赋值给了Interop的Interior.Pattern(图案样式枚举),完全搞反了属性!Interop的Pattern需要的是填充样式(比如纯色、灰度50%),而不是颜色。
  2. 边框LineStyle报错:ClosedXml的XLBorderStyleValues枚举和Interop的XlLineStyle枚举值不一一对应,比如ClosedXml的Thin是一个枚举值,而Interop里的细边框是xlContinuous样式+xlThin粗细,直接赋值类型不兼容。

解决方案:枚举映射+属性正确对应

我们需要手动做枚举值的映射,同时修正属性的对应关系。

1. 编写枚举映射方法

先写几个工具方法,把ClosedXml的格式枚举转换成Interop对应的枚举:

// 映射ClosedXml填充图案到Excel Interop的XlPattern枚举
private Excel.XlPattern MapFillPattern(XLFillPatternType cxPattern)
{
    return cxPattern switch
    {
        XLFillPatternType.None => Excel.XlPattern.xlPatternNone,
        XLFillPatternType.Solid => Excel.XlPattern.xlPatternSolid,
        XLFillPatternType.Gray50 => Excel.XlPattern.xlPatternGray50,
        XLFillPatternType.Gray75 => Excel.XlPattern.xlPatternGray75,
        XLFillPatternType.Gray25 => Excel.XlPattern.xlPatternGray25,
        XLFillPatternType.HorizontalStripe => Excel.XlPattern.xlPatternHorizontal,
        XLFillPatternType.VerticalStripe => Excel.XlPattern.xlPatternVertical,
        // 其他需要的图案样式可以继续补充
        _ => Excel.XlPattern.xlPatternNone
    };
}

// 映射ClosedXml边框样式到Excel Interop的XlLineStyle枚举
private Excel.XlLineStyle MapBorderStyle(XLBorderStyleValues cxBorder)
{
    return cxBorder switch
    {
        XLBorderStyleValues.None => Excel.XlLineStyle.xlLineStyleNone,
        XLBorderStyleValues.Thin => Excel.XlLineStyle.xlContinuous,
        XLBorderStyleValues.Medium => Excel.XlLineStyle.xlContinuous,
        XLBorderStyleValues.Thick => Excel.XlLineStyle.xlContinuous,
        XLBorderStyleValues.Dashed => Excel.XlLineStyle.xlDash,
        XLBorderStyleValues.Dotted => Excel.XlLineStyle.xlDot,
        XLBorderStyleValues.DashDot => Excel.XlLineStyle.xlDashDot,
        XLBorderStyleValues.DashDotDot => Excel.XlLineStyle.xlDashDotDot,
        XLBorderStyleValues.Double => Excel.XlLineStyle.xlDouble,
        _ => Excel.XlLineStyle.xlLineStyleNone
    };
}

// 映射ClosedXml边框粗细到Excel Interop的XlBorderWeight枚举
private Excel.XlBorderWeight MapBorderWeight(XLBorderStyleValues cxBorder)
{
    return cxBorder switch
    {
        XLBorderStyleValues.Thin => Excel.XlBorderWeight.xlThin,
        XLBorderStyleValues.Medium => Excel.XlBorderWeight.xlMedium,
        XLBorderStyleValues.Thick => Excel.XlBorderWeight.xlThick,
        _ => Excel.XlBorderWeight.xlThin
    };
}

2. 修正格式赋值代码

把原来的格式处理代码替换成下面的逻辑,注意属性的正确对应和枚举映射:

var xl = Globals.ThisAddIn.Application;
var dest = xl.ActiveWorkbook;
try {
    var org = new XLWorkbook(pfad);
    foreach (IXLWorksheet sheet in org.Worksheets) {
        var usedRange = sheet.RangeUsed(true);
        Excel.Worksheet dsheet = dest.Sheets[sheet.Name];
        
        foreach (IXLCell cel in usedRange.Cells(false)) {
            var adr = cel.Address.ToStringFixed(); // 确保地址格式和Interop兼容
            var destRange = dsheet.Range[adr];
            
            // --- 处理单元格填充格式 ---
            var cxFill = cel.Style.Fill;
            var interior = destRange.Interior;
            
            // 处理背景色(区分主题色和普通颜色)
            if (cxFill.BackgroundColor.IsTheme)
            {
                interior.ThemeColor = (Excel.XlThemeColor)(int)cxFill.BackgroundColor.Theme;
                interior.TintAndShade = cxFill.BackgroundColor.Tint;
            }
            else
            {
                interior.Color = cxFill.BackgroundColor.Color;
            }
            
            // 处理填充图案
            interior.Pattern = MapFillPattern(cxFill.PatternType);
            if (cxFill.PatternType != XLFillPatternType.None)
            {
                // 只有非无图案时才设置图案颜色
                if (cxFill.PatternColor.IsTheme)
                {
                    interior.PatternThemeColor = (Excel.XlThemeColor)(int)cxFill.PatternColor.Theme;
                    interior.PatternTintAndShade = cxFill.PatternColor.Tint;
                }
                else
                {
                    interior.PatternColor = cxFill.PatternColor.Color;
                }
            }
            
            // --- 处理边框格式 ---
            var cxBorder = cel.Style.Border;
            var borders = destRange.Borders;
            
            // 处理顶部边框
            var topBorder = borders[Excel.XlBordersIndex.xlEdgeTop];
            topBorder.LineStyle = MapBorderStyle(cxBorder.TopBorder);
            topBorder.Weight = MapBorderWeight(cxBorder.TopBorder);
            if (cxBorder.TopBorder != XLBorderStyleValues.None)
            {
                if (cxBorder.TopBorderColor.IsTheme)
                {
                    topBorder.ThemeColor = (Excel.XlThemeColor)(int)cxBorder.TopBorderColor.Theme;
                    topBorder.TintAndShade = cxBorder.TopBorderColor.Tint;
                }
                else
                {
                    topBorder.Color = cxBorder.TopBorderColor.Color;
                }
            }
            
            // 同理处理左、右、底部、内部边框(xlEdgeLeft、xlEdgeRight、xlEdgeBottom、xlInsideHorizontal等)
            // ... 这里可以复制顶部边框的逻辑,替换对应的边框枚举即可
        }
    }
    org.Dispose();
} catch (Exception ex) {
    log.WriteLine(ex.Message);
}

3. 额外优化建议

  • 减少Interop调用次数:逐个单元格处理性能还是有点慢,你可以尝试按行/列的区域批量复制格式,比如先获取ClosedXml中某一行的所有格式,然后一次性应用到Interop的对应行。
  • 主题色兼容:上面的代码已经处理了主题色和TintAndShade,这是很容易被忽略的点,确保格式还原的完整性。
  • 异常处理:可以给枚举映射加上默认值,避免出现未定义的枚举值导致崩溃。

内容的提问来源于stack exchange,提问作者LocEngineer

火山引擎 最新活动