最近需要用到WPF做一个报表
然后经过各种查阅资料
终于整出来了一个Demo
软件是VS2017 .netframework是4.5
1.配置环境
工具,获取扩展和更新
然后安装RDLC,然后重启
新建项目
项目的引用里面添加
System.Printing
System.Management
System.Drawing
NuGet包里面添加
Microsoft.ReportViewer V11.0.3366.16
2.创建实体类和数据集
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RDLCPrinter
{
public class Customer
{
public string carrierCompanyName { get; set; }
public string checkInTime { get; set; }
public string startPoint { get; set; }
public string outStock { get; set; }
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RDLCPrinter
{
class Plan
{
public string carrierCompanyName { get; set; } //承运单位
public string driver { get; set; } //司机
public string driverId { get; set; } //司机id
public string vehicleNo { get; set; } //车牌号
public string mobile { get; set; } //电话
public string planNo { get; set; } //调度单号
public string startPoint { get; set; } //装点
public string endPoint { get; set; } //卸点
public string flowName { get; set; } //流向
public string prodName { get; set; } //品名
public string planWeight { get; set; } //重量
public string planQuantity { get; set; } //件数
public string remark { get; set; } //备注
public string cardNo { get; set; }
public string planStatusName { get; set; }//签到状态
public string checkInTime { get; set; }//签到时间
public string scanDateTime { get; set; }//扫码日期和时间
public string scanTime { get; set; }//扫码时间
public string outStock { get; set; }//仓库
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
然后创建两个数据集
数据集里 右键 添加 DataTable把实体类的属性都加进去
或者
工具箱 双击DataTable
加完之后是这样的
然后创建一个rdlc
左边 新建 数据集
自己找到实体类,或者直接选择刚才创建好的DataSet.xsd
两个实体类都添加一下数据源
然后在rdlc文件 右键 插入 表
表里绑定一下属性
表头的汉字是自己加的
新建几个文本框 还有创建一个页眉
一个文本框绑定页数
另外的绑定另外一个实体类
具体的操作流程
在一个文本框上面右键
数据集->DataSet->然后选择属性双击
绑定页数同理,用的是内置字段->pageNumber双击,然后用一下字符串拼接
(这个感觉和Excel非常神似啊)
3.页面设计
MainWindow添加两个按钮,一个是打印预览的,一个是直接打印的
<Window x:Class="RDLCPrinter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rv="clr-namespace:Microsoft.Reporting.WinForms;assembly=Microsoft.ReportViewer.WinForms"
xmlns:local="clr-namespace:RDLCPrinter"
mc:Ignorable="d"
Title="MainWindow" Height="324" Width="576">
<Grid>
<DockPanel>
<Button Width="100" Height="30" Content="打印预览" Click="btnClickMe_Click" Margin="90,0,0,0"/>
<Button x:Name="Button_Printer" Width="100" Height="30" Content="直接打印" Click="Button_Print_Click"/>
</DockPanel>
</Grid>
</Window>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
然后是RepoerViewer.xaml
这个需要注意的是
需要在头上加上这个
xmlns:rv="clr-namespace:Microsoft.Reporting.WinForms;assembly=Microsoft.ReportViewer.WinForms"
- 1
然后是整体的代码
<UserControl x:Class="RDLCPrinter.ReportViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rv="clr-namespace:Microsoft.Reporting.WinForms;assembly=Microsoft.ReportViewer.WinForms"
xmlns:local="clr-namespace:RDLCPrinter"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="800" Loaded="UserControl_Loaded">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<WindowsFormsHost Name="printArea">
<rv:ReportViewer x:Name="reportViewer1" RenderingComplete="reportViewer_RenderingComplete" />
</WindowsFormsHost>
</Grid>
</UserControl>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
然后新建一个CustomerReport.xaml
这个是打印预览界面
调一下刚才的ReportViewer
<Window x:Class="RDLCPrinter.CustomerReport"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RDLCPrinter"
mc:Ignorable="d"
Title="CustomerReport" Height="600" Width="800">
<Grid>
<local:ReportViewer ></local:ReportViewer>
</Grid>
</Window>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
4.数据注入
这一步是最主要的
ReportViewer.cs注入打印预览界面的数据
然后mainWindow注入直接打印的数据
using System.Data;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Reporting.WinForms;
namespace RDLCPrinter
{
/// <summary>
/// Interaction logic for ReportViewer.xaml
/// </summary>
public partial class ReportViewer : UserControl
{
public ReportViewer()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
DataTable dt2 = new DataTable();
dt2.Columns.Add(new DataColumn("carrierCompanyName", typeof(string)));
dt2.Columns.Add(new DataColumn("checkInTime", typeof(string)));
dt2.Columns.Add(new DataColumn("startPoint", typeof(string)));
dt2.Columns.Add(new DataColumn("outStock", typeof(string)));
DataRow dr2 = dt2.NewRow();
dr2["carrierCompanyName"] = "日照钢铁";
dr2["checkInTime"] = "2019-8-13 16:03";
dr2["startPoint"] = "山东青岛";
dr2["outStock"] = "Z1-热轧#1580成品库";
dt2.Rows.Add(dr2);
ReportDataSource reportDataSource2 = new ReportDataSource();
reportDataSource2.Name = "DataSet1";
reportDataSource2.Value = dt2;
reportViewer1.LocalReport.DataSources.Add(reportDataSource2);
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("carrierCompanyName", typeof(string)));
dt.Columns.Add(new DataColumn("driverId", typeof(string)));
dt.Columns.Add(new DataColumn("startPoint", typeof(string)));
dt.Columns.Add(new DataColumn("endPoint", typeof(string)));
dt.Columns.Add(new DataColumn("prodName", typeof(string)));
dt.Columns.Add(new DataColumn("planQuantity", typeof(string)));
dt.Columns.Add(new DataColumn("checkInTime", typeof(string)));
dt.Columns.Add(new DataColumn("outStock", typeof(string)));
DataRow dr = dt.NewRow();
for (int i = 0; i < 20; i++)
{
dt.Rows.Add(new string[] { ("京创" + i).ToString(), (i + 10).ToString(), ("日照"+i).ToString(),
("山东" +i).ToString(),"卷钢" + i.ToString(),(10+i).ToString(),("2019-8-1"+i).ToString(), (i+"号").ToString()});
}
ReportDataSource reportDataSource = new ReportDataSource();
reportDataSource.Name = "DataSet2";
reportDataSource.Value = dt;
reportViewer1.LocalReport.DataSources.Add(reportDataSource);
reportViewer1.LocalReport.ReportPath = "D:\\Documents\\GitHub\\DEMO\\RDLC\\RDLC\\CustomerReport.rdlc";
//刷新
reportViewer1.RefreshReport();
}
private void reportViewer_RenderingComplete(object sender, Microsoft.Reporting.WinForms.RenderingCompleteEventArgs e)
{
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
直接打印用到了一个工具类
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Reporting.WinForms;
namespace RDLCPrinter
{
/// <summary>
/// 通过RDLC向默认打印机输出打印报表
/// </summary>
public class BillPrint : IDisposable
{
/// <summary>
/// 当前打印页号
/// </summary>
static int m_currentPageIndex;
/// <summary>
/// RDCL转换stream一页对应一个stream
/// </summary>
static List<Stream> m_streams;
/// <summary>
/// 把report输出成stream
/// </summary>
/// <param name="report">传入需要Export的report</param>
private void Export(LocalReport report)
{
string deviceInfo =
"<DeviceInfo>" +
" <OutputFormat>EMF</OutputFormat>" +
//" <PageWidth>2in</PageWidth>" +
//" <PageHeight>20in</PageHeight>" +
" <MarginTop>1in</MarginTop>" +
" <MarginLeft>0in</MarginLeft>" +
" <MarginRight>0in</MarginRight>" +
" <MarginBottom>1in</MarginBottom>" +
"</DeviceInfo>";
Warning[] warnings;
m_streams = new List<Stream>();
report.Render("Image", deviceInfo, CreateStream, out warnings);
foreach (Stream stream in m_streams)
stream.Position = 0;
}
/// <summary>
/// 创建具有指定的名称和格式的流。
/// </summary>
private Stream CreateStream(string name, string fileNameExtension,
Encoding encoding, string mimeType, bool willSeek)
{
Stream stream = new FileStream(name + "." + fileNameExtension,
FileMode.Create);
m_streams.Add(stream);
return stream;
}
/// <summary>
/// 打印输出
/// </summary>
private void PrintPage(object sender, PrintPageEventArgs ev)
{
Metafile pageImage =
new Metafile(m_streams[m_currentPageIndex]);
ev.Graphics.DrawImage(pageImage, ev.PageBounds);
m_currentPageIndex++;
ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
}
/// <summary>
/// 打印预处理
/// </summary>
private void Print()
{
PrintDocument printDoc = new PrintDocument();
string printerName = printDoc.PrinterSettings.PrinterName;
if (m_streams == null || m_streams.Count == 0)
return;
printDoc.PrinterSettings.PrinterName = printerName;
if (!printDoc.PrinterSettings.IsValid)
{
string msg = String.Format("Can‘t find printer \"{0}\".", printerName);
throw new Exception(msg);
}
printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
StandardPrintController spc = new StandardPrintController();
printDoc.PrintController = spc;
printDoc.Print();
}
public void Dispose()
{
if (m_streams != null)
{
foreach (Stream stream in m_streams)
stream.Close();
m_streams = null;
}
}
/// <summary>
/// 对外接口,启动打印
/// </summary>
/// <param name="dtSource">打印报表对应的数据源</param>
/// <param name="sReport">打印报表名称</param>
public static void Run(LocalReport report)
{
//LocalReport report = new LocalReport();
//report.ReportPath = @"..\..\" + sReport;
//ReportDataSource dataset = new ReportDataSource("DataSet1", dtSource);
//report.DataSources.Add(dataset);
m_currentPageIndex = 0;
BillPrint billPrint = new BillPrint();
billPrint.Export(report);
billPrint.Print();
billPrint.Dispose();
}
/// <summary>
/// 获取打印机状态
/// </summary>
/// <param name="printerName">打印机名称</param>
/// <param name="status">输出打印机状态</param>
private static void GetPrinterStatus2(string printerName, ref uint status)
{
try
{
string lcPrinterName = printerName;
IntPtr liHandle = IntPtr.Zero;
if (!Win32.OpenPrinter(lcPrinterName, out liHandle, IntPtr.Zero))
{
Console.WriteLine("print is close");
return;
}
UInt32 level = 2;
UInt32 sizeNeeded = 0;
IntPtr buffer = IntPtr.Zero;
Win32.GetPrinter(liHandle, level, buffer, 0, out sizeNeeded);
buffer = Marshal.AllocHGlobal((int)sizeNeeded);
if (!Win32.GetPrinter(liHandle, level, buffer, sizeNeeded, out sizeNeeded))
{
Console.WriteLine(Environment.NewLine + "Fail GetPrinter:" + Marshal.GetLastWin32Error());
return;
}
Win32.PRINTER_INFO_2 info = (Win32.PRINTER_INFO_2)Marshal.PtrToStructure(buffer, typeof(Win32.PRINTER_INFO_2));
status = info.Status;
Marshal.FreeHGlobal(buffer);
Win32.ClosePrinter(liHandle);
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 对外接口,调去打印机信息
/// </summary>
/// <param name="printerName">打印机名称</param>
/// <returns>返回打印机当前状态</returns>
public static string GetPrinterStatus(string printerName)
{
uint intValue = 0;
PrintDocument pd = new PrintDocument();
printerName = printerName == "" ? pd.PrinterSettings.PrinterName : printerName;
GetPrinterStatus2(printerName, ref intValue);
string strRet = string.Empty;
switch (intValue)
{
case 0:
strRet = "准备就绪(Ready)";
break;
case 4194432:
strRet = "被打开(Lid Open)";
break;
case 144:
strRet = "打印纸用完(Out of Paper)";
break;
case 4194448:
strRet = "被打开并且打印纸用完(Out of Paper && Lid Open)";
break;
case 1024:
strRet = "打印中(Printing)";
break;
case 32768:
strRet = "初始化(Initializing)";
break;
case 160:
strRet = "手工送纸(Manual Feed in Progress)";
break;
case 4096:
strRet = "脱机(Offline)";
break;
default:
strRet = "未知状态(unknown state)";
break;
}
return strRet;
}
}
public class Win32
{
[DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool OpenPrinter(string printer, out IntPtr handle, IntPtr printerDefaults);
[DllImport("winspool.drv")]
public static extern bool ClosePrinter(IntPtr handle);
[DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetPrinter(IntPtr handle, UInt32 level, IntPtr buffer, UInt32 size, out UInt32 sizeNeeded);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct PRINTER_INFO_2
{
public string pServerName;
public string pPrinterName;
public string pShareName;
public string pPortName;
public string pDriverName;
public string pComment;
public string pLocation;
public IntPtr pDevMode;
public string pSepFile;
public string pPrintProcessor;
public string pDatatype;
public string pParameters;
public IntPtr pSecurityDescriptor;
public UInt32 Attributes;
public UInt32 Priority;
public UInt32 DefaultPriority;
public UInt32 StartTime;
public UInt32 UntilTime;
public UInt32 Status;
public UInt32 cJobs;
public UInt32 AveragePPM;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
判断打印机状态好像没啥用
最后是MainWindow的数据
using Microsoft.Reporting.WinForms;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace RDLCPrinter
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnClickMe_Click(object sender, RoutedEventArgs e)
{
CustomerReport rpt = new CustomerReport();
rpt.ShowDialog();
}
//打印
private void Button_Print_Click(object sender, RoutedEventArgs e)
{
ReportViewer rvDoc = new ReportViewer();
DataTable dt2 = new DataTable();
dt2.Columns.Add(new DataColumn("carrierCompanyName", typeof(string)));
dt2.Columns.Add(new DataColumn("checkInTime", typeof(string)));
dt2.Columns.Add(new DataColumn("startPoint", typeof(string)));
dt2.Columns.Add(new DataColumn("outStock", typeof(string)));
DataRow dr2 = dt2.NewRow();
dr2["carrierCompanyName"] = "日照钢铁";
dr2["checkInTime"] = "2019-8-13 16:03";
dr2["startPoint"] = "山东青岛";
dr2["outStock"] = "Z1-热轧#1580成品库";
dt2.Rows.Add(dr2);
ReportDataSource reportDataSource2 = new ReportDataSource();
reportDataSource2.Name = "DataSet1";
reportDataSource2.Value = dt2;
rvDoc.reportViewer1.LocalReport.DataSources.Add(reportDataSource2);
//rvDoc.reportViewer1
//rvDoc.reportViewer1.LocalReport.DataSources.Add(reportDataSource2);
rvDoc.reportViewer1.LocalReport.ReportPath = @"D:\\Documents\\GitHub\\DEMO\\RDLC\\RDLC\\CustomerReport.rdlc";
//rvDoc.reportViewer1.RefreshReport();
/*自动打印*/
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("carrierCompanyName", typeof(string)));
dt.Columns.Add(new DataColumn("driverId", typeof(string)));
dt.Columns.Add(new DataColumn("startPoint", typeof(string)));
dt.Columns.Add(new DataColumn("endPoint", typeof(string)));
dt.Columns.Add(new DataColumn("prodName", typeof(string)));
dt.Columns.Add(new DataColumn("planQuantity", typeof(string)));
dt.Columns.Add(new DataColumn("checkInTime", typeof(string)));
dt.Columns.Add(new DataColumn("outStock", typeof(string)));
DataRow dr = dt.NewRow();
for (int i = 0; i < 20; i++)
{
dt.Rows.Add(new string[] { ("京创" + i).ToString(), (i + 10).ToString(), ("日照"+i).ToString(),
("山东" +i).ToString(),"卷钢" + i.ToString(),(10+i).ToString(),("2019-8-1"+i).ToString(), (i+"号").ToString()});
}
ReportDataSource reportDataSource = new ReportDataSource();
reportDataSource.Name = "DataSet2";
reportDataSource.Value = dt;
rvDoc.reportViewer1.LocalReport.DataSources.Add(reportDataSource);
RDLCPrinter.BillPrint.Run(rvDoc.reportViewer1.LocalReport);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
5.运行预览
准备工作结束之后
开始运行
点击打印预览
点击直接打印就直接打印了
上图
手机像素比较差
然后是项目下载地址
链接:https://pan.baidu.com/s/15p_qbIlgP5FQslTSYtqoSA
提取码:9xfp
6.注意点
1.打印预览如果正常,但是打印出来多余空白页的话
如下设置
点击到rdlc文件,然后右边的属性,选择报表
然后把下面的ConsumeContainerWhite设置为true
2.这个clsReportPrint类是多余的,下载下来可以不用管