起因
在开发中经常遇到一些数据导出成文件的需求,客户有时就是要个小表格。数据集导出为Excel格式的文件的方法太多了。估计常见的有十几种。这里是讲一个另类的简单方法。
结果
先上例子 :
先创建一个测试用的数据集,生成4个字段15行的测试数据。
DataTable dt = new DataTable();
dt.Columns.AddRange(new[] {
new DataColumn("Name", typeof(string)),
new DataColumn("Age", typeof(int)),
new DataColumn("Date", typeof(string)),
new DataColumn("Active", typeof(bool)) });
for (int i = 1; i <= 15; i++)
dt.Rows.Add( $"User{i}", new Random().Next(18, 65), DateTime.Now.AddDays(-i).ToString("yyyy-MM-dd"), i % 3 != 0);
然后生成导出的格式。导出的格式为rdlc报表,实际就是一个xml格式的文本。
然后 加载rdlc,并传入测试数据集。
RdlcGenerator rg = new RdlcGenerator();//这个是一个简单的xml生成
string rdlcXml = rg.GenerateRdlcXml(dt); //System.IO.File.WriteAllText(@"d:\1.rdlc", rdlcXml);
var report = new LocalReport();
report.LoadReportDefinition(new System.IO.StringReader(rdlcXml)); //加载rdlc
report.DataSources.Add(new ReportDataSource("DataSet1", dt)); //传入数据集
RdlcGenerator rg = new RdlcGenerator();
string rdlcXml = rg.GenerateRdlcXml(dt); //System.IO.File.WriteAllText(@"d:\1.rdlc", rdlcXml);
var report = new LocalReport();
report.LoadReportDefinition(new System.IO.StringReader(rdlcXml));
report.DataSources.Add(new ReportDataSource("DataSet1", dt));
最后导出文件,这里列举了常见的几种格式,实际支持更多的格式。
byte[] result = report.Render("EXCELOPENXML");
System.IO.File.WriteAllBytes(@"d:\1.xlsx", result);
result = report.Render("PDF");
System.IO.File.WriteAllBytes(@"d:\1.pdf", result);
result = report.Render("WORDOPENXML");
System.IO.File.WriteAllBytes(@"d:\1.docx", result);
result = report.Render("IMAGE");
System.IO.File.WriteAllBytes(@"d:\1.tiff", result);
支持 常见的 xlsx、pdf、docx和图片
Web 或者 winform都支持 ,在nuget时现在不同的包。
设计
导出的文件的颜色、字体、边框啥的,可以随便改。
如果觉得直接改这个文本内容比较麻烦,其实有个简单的方法。
前面的代码里 ,俺有一行注释 //System.IO.File.WriteAllText(@"d:\1.rdlc", rdlcXml); 其实就是把rdlc的设计保存到文件,保存到文件之后就可以使用VS的 报表设计器 编辑样式,然后把 颜色、字体、边框啥的复制回去。
其实rdlc 就是个 xml文件。方便的很。所以这个例子就是一个动态生成rdlc的例子,例子只是简单的演示功能,真正使用时,可以自己设计一些rdlc模板。再在这些模版上动态添加内容。
代码
先 Nuget Microsoft.ReportingServices.Report...
Web 或者 winform都支持
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace WindowsFormsAppRPT
{
public class RdlcGenerator
{
public List<ColDef> _collist = new List<ColDef>();
public class ColDef
{
public string title = "";
public string field = "";
public double width_cm = 3.2;
}
public void AddCol(string title, string field,double width_cm)
{
_collist.Add(new ColDef() { title = title, field= field, width_cm= width_cm });
}
public string GenerateRdlcXml(DataTable dataTable )
{
if (_collist.Count <= 0)
{
foreach (DataColumn dc in dataTable.Columns)
{
AddCol(dc.ColumnName, dc.ColumnName,2);
}
}
XmlDocument doc = new XmlDocument();
doc.LoadXml(GetBlankRdlc());
XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
nsMgr.AddNamespace("ns", "http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition");
XmlNode body = doc.SelectSingleNode("ns:Report/ns:Body", nsMgr);
body.InnerXml = @" <ReportItems>
<Tablix Name=""Tablix1"">
<TablixBody>
<TablixColumns>
</TablixColumns>
<TablixRows>
<TablixRow>
<Height>0.6cm</Height>
<TablixCells>
</TablixCells>
</TablixRow>
<TablixRow>
<Height>0.6cm</Height>
<TablixCells>
</TablixCells>
</TablixRow>
</TablixRows>
</TablixBody>
<TablixColumnHierarchy>
<TablixMembers>
</TablixMembers>
</TablixColumnHierarchy>
<TablixRowHierarchy>
<TablixMembers>
<TablixMember>
<KeepWithGroup>After</KeepWithGroup>
</TablixMember>
<TablixMember>
<Group Name=""详细信息"" />
</TablixMember>
</TablixMembers>
</TablixRowHierarchy>
<DataSetName>DataSet1</DataSetName>
<Height>1.2cm</Height>
<Width>2.5cm</Width>
<Style>
<Border>
<Style>None</Style>
</Border>
</Style>
</Tablix>
</ReportItems>
<Height>0.47244in</Height>
<Style />";
XmlNode TablixColumns = doc.SelectSingleNode("//ns:*/ns:TablixColumns", nsMgr);
XmlNode TablixMembers = doc.SelectSingleNode("//ns:*/ns:TablixMembers", nsMgr);
StringBuilder sb_cols = new StringBuilder();
StringBuilder sb_mems = new StringBuilder();
foreach (ColDef col in _collist)
{
sb_cols.AppendLine(@"<TablixColumn>
<Width>"+col.width_cm.ToString()+ @"cm</Width>
</TablixColumn>");
sb_mems.AppendLine(@"<TablixMember />");
}
TablixColumns.InnerXml = sb_cols.ToString();
TablixMembers.InnerXml = sb_mems.ToString();
XmlNodeList TablixCells = doc.SelectNodes("//ns:*/ns:TablixCells", nsMgr);
XmlNode header = TablixCells[0];
XmlNode data_row = TablixCells[1];
StringBuilder sb_header = new StringBuilder();
int idx = 0;
foreach(ColDef col in _collist)
{
idx++;
sb_header.AppendLine(@"<TablixCell>
<CellContents>
<Textbox Name=""Textbox_header"+idx.ToString()+@""">
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value>"+col.title+ @"</Value>
<Style>
<FontWeight>Bold</FontWeight>
</Style>
</TextRun>
</TextRuns>
<Style>
<TextAlign>Center</TextAlign>
</Style>
</Paragraph>
</Paragraphs>
<rd:DefaultName>Textbox_header" + idx.ToString() + @"</rd:DefaultName>
<Style>
<Border>
<Color>LightGrey</Color>
<Style>Solid</Style>
<Width>0.5pt</Width>
</Border>
<BackgroundColor>PaleTurquoise</BackgroundColor>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>");
}
header.InnerXml = sb_header.ToString();
StringBuilder sb_data_row = new StringBuilder();
idx = 0;
foreach (ColDef col in _collist)
{
idx++;
sb_data_row.AppendLine(@"<TablixCell>
<CellContents>
<Textbox Name=""DataColumn"+idx.ToString()+@""">
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value>=Fields!"+col.field+ @".Value</Value>
<Style />
</TextRun>
</TextRuns>
<Style />
</Paragraph>
</Paragraphs>
<rd:DefaultName>" + col.field + @"</rd:DefaultName>
<Style>
<Border>
<Color>LightGrey</Color>
<Style>Solid</Style>
<Width>0.5pt</Width>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>");
}
data_row.InnerXml = sb_data_row.ToString();
XmlNode fields = doc.SelectSingleNode("ns:Report/ns:DataSets/ns:DataSet/ns:Fields", nsMgr);
StringBuilder sb_fields = new StringBuilder();
foreach (DataColumn dc in dataTable.Columns)
{
sb_fields.AppendLine(@"<Field Name="""+dc.ColumnName+@""">");
sb_fields.AppendLine(@"<DataField>" + dc.ColumnName + "</DataField>");
sb_fields.AppendLine(@"<rd:TypeName>"+dc.DataType.FullName+"</rd:TypeName>");
sb_fields.AppendLine(@"</Field>");
}
fields.InnerXml = sb_fields.ToString();
using (StringWriter sw = new StringWriter())
{
using (XmlTextWriter writer = new XmlTextWriter(sw))
{
writer.Formatting = Formatting.Indented;
writer.Indentation = 4;
doc.WriteTo(writer);
return sw.ToString();
}
}
}
public static string GetBlankRdlc()
{
return @"<?xml version=""1.0"" encoding=""utf-8""?>
<Report xmlns=""http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition"" xmlns:rd=""http://schemas.microsoft.com/SQLServer/reporting/reportdesigner"">
<Body>
</Body>
<Width>2.95276in</Width>
<Page>
<PageHeight>29.7cm</PageHeight>
<PageWidth>21cm</PageWidth>
<LeftMargin>1cm</LeftMargin>
<RightMargin>1cm</RightMargin>
<TopMargin>1cm</TopMargin>
<BottomMargin>1cm</BottomMargin>
<ColumnSpacing>0.13cm</ColumnSpacing>
<Style />
</Page>
<AutoRefresh>0</AutoRefresh>
<DataSources>
<DataSource Name=""DataSet1"">
<ConnectionProperties>
<DataProvider>System.Data.DataSet</DataProvider>
<ConnectString>/* Local Connection */</ConnectString>
</ConnectionProperties>
<rd:DataSourceID>"+Guid.NewGuid().ToString()+ @"</rd:DataSourceID>
</DataSource>
</DataSources>
<DataSets>
<DataSet Name=""DataSet1"">
<Query>
<DataSourceName>DataSet1</DataSourceName>
<CommandText>/* Local Query */</CommandText>
</Query>
<Fields>
</Fields>
<rd:DataSetInfo />
</DataSet>
</DataSets>
<rd:ReportUnitType>Cm</rd:ReportUnitType>
<rd:ReportID>" + Guid.NewGuid().ToString() + @"</rd:ReportID>
</Report>";
}
}
}