4

我想知道是否可以将 Excel 输出设置为“锁定”,因为当我们尝试更改单元格的值时,会出现警告,表明我们无法更改它,除非我们删除表的保护。

我知道我们可以开发自定义 Excel 自动化代码,并在保存工作表之前设置密码来保护工作表。但是,有没有什么简单的方法可以使用 ReportViewer 的内置功能来实现这一点?

4

1 回答 1

8

经过一番研究,我设法找到了解决方案:)想法是拦截ReportViewer的Export Report功能,然后运行我们自己的进程。此过程将获得正在生成的 Excel 文件的输出,然后读取它并应用任何必要的更改,并在将其作为下载发送给用户之前再次保存。但是应该注意的是,拦截方法会根据我们使用的报告类型而有所不同。就我而言,我的 ReportViewer 使用的是 WebForm 而不是 WinForm,并且大多数解释都是关于 ReportExport 事件的解释,该事件仅在 WinForm 中可用。

对于使用 WinForm 的用户,您可以像这样覆盖 ReportExport 事件:

void reportViewer_ReportExport(object sender, Microsoft.Reporting.WinForms.ReportExportEventArgs e)
{
    e.Cancel = true;
    // insert your own code to export excel
}

在 WebForm 中,没有 ReportExport 的事件处理程序。我能想到的选项是在 .aspx 中创建一个自定义按钮,它将执行我们的自定义代码,或者直接呈现 excel 而无需预览报告。我决定直接渲染excel文件。我将使用数据集并从存储过程中获取数据。然后,我将数据集分配给 RDLC 并调用 Render 方法来获取输出。输出格式为byte[],我用FileStream来写。完成后,我使用 Interop 打开 Excel 文件并应用保护。这是代码:

// Setup DataSet (Adapter and Table)
YourTableAdapters.ATableAdapter ds = new YourTableAdapters.ATableAdapter();
YourDataSet.ADataTable dt = new YourDataSet.ADataTable ();

ds.Fill(dt, outlet, period);

// Create Report DataSource
ReportDataSource rds = new ReportDataSource("DataSet1", (System.Data.DataTable)dt);

// Variables needed for ReportViewer Render method
Warning[] warnings;
string[] streamIds;
string mimeType = string.Empty;
string encoding = string.Empty;
string extension = string.Empty;

// Setup the report viewer object and get the array of bytes
ReportViewer viewer = new ReportViewer();
viewer.ProcessingMode = ProcessingMode.Local;
viewer.LocalReport.ReportPath = "YourReport.rdlc";
viewer.LocalReport.DataSources.Add(rds); // Add datasource here

byte[] bytes = viewer.LocalReport.Render("Excel", null, out mimeType,
                                          out encoding, out extension,
                                          out streamIds, out warnings);

// Prepare filename and save_path, and then write the Excel using FileStream.
String temp_path = Path.Combine(Server.MapPath(Config.ReportPath), "FileName.xls");
FileStream fs = new FileStream(temp_path, FileMode.Create);
fs.Write(bytes, 0, bytes.Length);
fs.Close();

// Open the Excel file created, and add password protection.
PIDExcel pidexcel = new PIDExcel();
pidexcel.CollectExcelPID();

Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Range lock_range = null;

int excelid = pidexcel.GetNewExcelID();

Microsoft.Office.Interop.Excel.Workbook xlWorkBook = null;

try
{
    //xlApp.Visible = true;
    xlWorkBook = (Microsoft.Office.Interop.Excel.Workbook)xlApp.Workbooks.Open(temp_path,
                  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                  Type.Missing, Type.Missing);

    foreach(Microsoft.Office.Interop.Excel.Worksheet displayWorksheet in xlApp.ActiveWorkbook.Worksheets)
    {
        lock_range = xlApp.Cells;
        lock_range.Select();
        lock_range.EntireColumn.Locked = true;
        displayWorksheet.Protect("<your password here>");
    }

}
catch (Exception ex)
{
    throw new Exception(ex.Message.Replace("'", "")); ;
}
finally
{
    // Set First Sheet Active
    xlWorkBook.Sheets[1].Select();
    xlApp.DisplayAlerts = false;
    xlWorkBook.Save();
    xlWorkBook.Close(Type.Missing, Type.Missing, Type.Missing);
    xlApp.Quit();

    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlWorkBook);
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlApp);

    GC.WaitForPendingFinalizers();
    GC.Collect();

    pidexcel.KillExcel(excelid);

}

通过使用这个概念,我可以轻松地设计报表输出,因为我使用 RDLC 作为模板来填充 SP 提供的数据,然后渲染它。想象一下,如果我们使用 Excel 手动编写报告代码(设置边框、合并单元格、分组)会很麻烦。

于 2012-09-07T09:09:07.487 回答