├── .gitattributes
├── .gitignore
├── Example
├── App.config
├── App.xaml
├── App.xaml.cs
├── Assets
│ └── pinterest_board_photo.png
├── Invoice.csproj
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
└── packages.config
├── Files
├── input.png
├── output.pdf
├── output1.png
└── output2.png
├── LICENSE.md
├── README.md
├── SimpleWPFReporting.sln
└── SimpleWPFReporting
├── Properties
└── AssemblyInfo.cs
├── Report.cs
├── ReportOrientation.cs
├── ReportPage.cs
├── SimpleWPFReporting.csproj
└── packages.config
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Windows Store app package directory
170 | AppPackages/
171 | BundleArtifacts/
172 |
173 | # Visual Studio cache files
174 | # files ending in .cache can be ignored
175 | *.[Cc]ache
176 | # but keep track of directories ending in .cache
177 | !*.[Cc]ache/
178 |
179 | # Others
180 | ClientBin/
181 | [Ss]tyle[Cc]op.*
182 | ~$*
183 | *~
184 | *.dbmdl
185 | *.dbproj.schemaview
186 | *.pfx
187 | *.publishsettings
188 | node_modules/
189 | orleans.codegen.cs
190 |
191 | # RIA/Silverlight projects
192 | Generated_Code/
193 |
194 | # Backup & report files from converting an old project file
195 | # to a newer Visual Studio version. Backup files are not needed,
196 | # because we have git ;-)
197 | _UpgradeReport_Files/
198 | Backup*/
199 | UpgradeLog*.XML
200 | UpgradeLog*.htm
201 |
202 | # SQL Server files
203 | *.mdf
204 | *.ldf
205 |
206 | # Business Intelligence projects
207 | *.rdl.data
208 | *.bim.layout
209 | *.bim_*.settings
210 |
211 | # Microsoft Fakes
212 | FakesAssemblies/
213 |
214 | # GhostDoc plugin setting file
215 | *.GhostDoc.xml
216 |
217 | # Node.js Tools for Visual Studio
218 | .ntvs_analysis.dat
219 |
220 | # Visual Studio 6 build log
221 | *.plg
222 |
223 | # Visual Studio 6 workspace options file
224 | *.opt
225 |
226 | # Visual Studio LightSwitch build output
227 | **/*.HTMLClient/GeneratedArtifacts
228 | **/*.DesktopClient/GeneratedArtifacts
229 | **/*.DesktopClient/ModelManifest.xml
230 | **/*.Server/GeneratedArtifacts
231 | **/*.Server/ModelManifest.xml
232 | _Pvt_Extensions
233 |
234 | # LightSwitch generated files
235 | GeneratedArtifacts/
236 | ModelManifest.xml
237 |
238 | # Paket dependency manager
239 | .paket/paket.exe
240 |
241 | # FAKE - F# Make
242 | .fake/
243 |
--------------------------------------------------------------------------------
/Example/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Example/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace Invoice
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Example/Assets/pinterest_board_photo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maximcus/SimpleWPFReporting/3c9bd334f9c210f0caf5549a6309894033226e5a/Example/Assets/pinterest_board_photo.png
--------------------------------------------------------------------------------
/Example/Invoice.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {DA07A6F7-D067-4E08-9ACB-621672357E2E}
8 | WinExe
9 | Invoice
10 | Invoice
11 | v4.6
12 | 512
13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 4
15 | true
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 | ..\packages\MaterialDesignColors.1.1.2\lib\net45\MaterialDesignColors.dll
40 |
41 |
42 | ..\packages\MaterialDesignThemes.2.5.1\lib\net45\MaterialDesignThemes.Wpf.dll
43 |
44 |
45 | ..\packages\PdfSharp-WPF.SimpleWPFReporting.1.0.0\lib\net45\PdfSharp-WPF.dll
46 |
47 |
48 | ..\packages\PdfSharp.Xps.SimpleWPFReporting.1.0.0\lib\net45\PdfSharp.Xps.dll
49 |
50 |
51 | ..\packages\SimpleWPFReporting.1.1.0\lib\net45\SimpleWPFReporting.dll
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | 4.0
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | MSBuild:Compile
71 | Designer
72 |
73 |
74 | MSBuild:Compile
75 | Designer
76 |
77 |
78 | App.xaml
79 | Code
80 |
81 |
82 | MainWindow.xaml
83 | Code
84 |
85 |
86 |
87 |
88 | Code
89 |
90 |
91 | True
92 | True
93 | Resources.resx
94 |
95 |
96 | True
97 | Settings.settings
98 | True
99 |
100 |
101 | ResXFileCodeGenerator
102 | Resources.Designer.cs
103 |
104 |
105 |
106 | SettingsSingleFileGenerator
107 | Settings.Designer.cs
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/Example/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
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 |
--------------------------------------------------------------------------------
/Example/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Data;
9 | using System.Windows.Documents;
10 | using System.Windows.Input;
11 | using System.Windows.Media;
12 | using System.Windows.Media.Imaging;
13 | using System.Windows.Navigation;
14 | using System.Windows.Shapes;
15 | using SimpleWPFReporting;
16 |
17 | namespace Invoice
18 | {
19 | ///
20 | /// Interaction logic for MainWindow.xaml
21 | ///
22 | public partial class MainWindow : Window
23 | {
24 | public MainWindow()
25 | {
26 | InitializeComponent();
27 | }
28 |
29 | private void Button_Click(object sender, RoutedEventArgs e)
30 | {
31 | try
32 | {
33 | Report.ExportVisualAsPdf(print);
34 |
35 | }
36 | finally
37 | {
38 | this.IsEnabled = true;
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Example/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("Invoice")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("Invoice")]
15 | [assembly: AssemblyCopyright("Copyright © 2019")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/Example/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Invoice.Properties
12 | {
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Returns the cached ResourceManager instance used by this class.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Invoice.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// Overrides the current thread's CurrentUICulture property for all
56 | /// resource lookups using this strongly typed resource class.
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Example/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/Example/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Invoice.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Example/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Example/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Files/input.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maximcus/SimpleWPFReporting/3c9bd334f9c210f0caf5549a6309894033226e5a/Files/input.png
--------------------------------------------------------------------------------
/Files/output.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maximcus/SimpleWPFReporting/3c9bd334f9c210f0caf5549a6309894033226e5a/Files/output.pdf
--------------------------------------------------------------------------------
/Files/output1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maximcus/SimpleWPFReporting/3c9bd334f9c210f0caf5549a6309894033226e5a/Files/output1.png
--------------------------------------------------------------------------------
/Files/output2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maximcus/SimpleWPFReporting/3c9bd334f9c210f0caf5549a6309894033226e5a/Files/output2.png
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Maxim Fedotov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SimpleWPFReporting
2 |
3 | Reporting in WPF (XAML) made easy
4 |
5 | ## How to get it
6 |
7 | SimpleWPFReporting package is available on [NuGet](https://www.nuget.org/packages/SimpleWPFReporting/)
8 |
9 | You can use Package Manager Console to get it:
10 |
11 | ~~~powershell
12 | PM> Install-Package SimpleWPFReporting
13 | ~~~
14 |
15 | ## How does it work
16 |
17 | This project gives you the ability to create any report with the full power of WPF XAML. SimpleWPFReporting will take care of exporting it as a PDF or printing it. The important point is that SimpleWPFReporting will not rasterize your report before exporting or printing it.
18 |
19 | In order to be able to divide any report into pages this project expects that you will use a vertical `StackPanel` as your report body. You should put your report elements into this `StackPanel`. SimpleWPFReporting accepts `StackPanel` as a parameter and automatically divides report elements into pages.
20 |
21 | ## How to use it
22 |
23 | The main API of SimpleWPFReporting consists of `Report` static class, `ExportReportAsPdf` method, and `PrintReport` method. Both of the methods accept the same arguments.
24 |
25 | ### Required arguments
26 |
27 | `StackPanel reportContainer` is your report body containing report elements.
28 |
29 | `object dataContext` is the data context of your report.
30 |
31 | `Thickness margin` is the margin applied to every report page. Both of the methods have overloads without the margin argument. They use `Thickness(25)` as a default margin.
32 |
33 | `ReportOrientation orientation` is the orientation (`Portrait` or `Landscape`) of your report.
34 |
35 | ### Optional arguments
36 |
37 | `ResourceDictionary resourceDictionary` provides all the resources used in the report.
38 |
39 | `Brush backgroundBrush` is the background brush of the report.
40 |
41 | `DataTemplate reportHeaderDataTemplate` is the `DataTemplate` used to create a header for every page of the report.
42 |
43 | `bool headerOnlyOnTheFirstPage` allows you to use a header only on the first page of the report. (default is false)
44 |
45 | `DataTemplate reportFooterDataTemplate` is the `DataTemplate` used to create a footer for every page of the report.
46 |
47 | `bool footerStartsFromTheSecondPage` allows you to not use a footer on the first page of the report. (default is false)
48 |
49 | ### Page number
50 |
51 | Every header and footer will be supplied with the `PageNumber` dynamic resource. You can use it as you wish. For example, this is the simplest possible footer DataTemplate:
52 |
53 | ```XML
54 |
55 |
56 |
57 | ```
58 |
59 | ## Example
60 |
61 | ### Report view:
62 | 
63 |
64 | ### Output PDF:
65 | 
66 | 
67 |
68 | As you can see there is no difference between input (XAML) and output (PDF) except for paging. The subtle difference in the font weight and the width of the lines is due to artifacts of scaling of the screenshots.
69 | PDF is not rasterized, as you can see for yourself: [pdf](Files/output.pdf).
70 |
--------------------------------------------------------------------------------
/SimpleWPFReporting.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29728.190
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleWPFReporting", "SimpleWPFReporting\SimpleWPFReporting.csproj", "{9846B28B-D22B-4B99-BFC1-420535730CE4}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1E03FFEC-44A5-4DF1-8CD6-7A5FBFECE9BA}"
9 | ProjectSection(SolutionItems) = preProject
10 | LICENSE.md = LICENSE.md
11 | README.md = README.md
12 | EndProjectSection
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Files", "Files", "{0E44BB82-F259-4F13-BA10-AB0A86A689B9}"
15 | ProjectSection(SolutionItems) = preProject
16 | Files\output.pdf = Files\output.pdf
17 | EndProjectSection
18 | EndProject
19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Images", "Images", "{AD970D11-F40F-43A0-BA1A-4BE4B38D45B2}"
20 | ProjectSection(SolutionItems) = preProject
21 | Files\input.png = Files\input.png
22 | Files\output1.png = Files\output1.png
23 | Files\output2.png = Files\output2.png
24 | EndProjectSection
25 | EndProject
26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Example", "Example", "{D89D75DC-4007-4F15-B1DA-F91E6C72CF72}"
27 | EndProject
28 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Invoice", "Example\Invoice.csproj", "{DA07A6F7-D067-4E08-9ACB-621672357E2E}"
29 | EndProject
30 | Global
31 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
32 | Debug|Any CPU = Debug|Any CPU
33 | Release|Any CPU = Release|Any CPU
34 | EndGlobalSection
35 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
36 | {9846B28B-D22B-4B99-BFC1-420535730CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {9846B28B-D22B-4B99-BFC1-420535730CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {9846B28B-D22B-4B99-BFC1-420535730CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {9846B28B-D22B-4B99-BFC1-420535730CE4}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {DA07A6F7-D067-4E08-9ACB-621672357E2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {DA07A6F7-D067-4E08-9ACB-621672357E2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {DA07A6F7-D067-4E08-9ACB-621672357E2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {DA07A6F7-D067-4E08-9ACB-621672357E2E}.Release|Any CPU.Build.0 = Release|Any CPU
44 | EndGlobalSection
45 | GlobalSection(SolutionProperties) = preSolution
46 | HideSolutionNode = FALSE
47 | EndGlobalSection
48 | GlobalSection(NestedProjects) = preSolution
49 | {AD970D11-F40F-43A0-BA1A-4BE4B38D45B2} = {0E44BB82-F259-4F13-BA10-AB0A86A689B9}
50 | {DA07A6F7-D067-4E08-9ACB-621672357E2E} = {D89D75DC-4007-4F15-B1DA-F91E6C72CF72}
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {C14426A7-1436-4D4D-8AC9-6D9D4F6DC212}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/SimpleWPFReporting/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("SimpleWPFReporting")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SimpleWPFReporting")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("9846b28b-d22b-4b99-bfc1-420535730ce4")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/SimpleWPFReporting/Report.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Printing;
6 | using System.Reflection;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Documents;
10 | using System.Windows.Markup;
11 | using System.Windows.Media;
12 | using System.Windows.Media.Imaging;
13 | using System.Windows.Xps;
14 | using System.Windows.Xps.Packaging;
15 | using Microsoft.Win32;
16 | using PdfSharp.Xps;
17 |
18 | #pragma warning disable 1591
19 | namespace SimpleWPFReporting
20 | {
21 | public static class Report
22 | {
23 | const int DIUPerInch = 96;
24 | private static readonly Thickness defaultMargin = new Thickness(25);
25 |
26 | public static void ExportVisualAsXps(Visual visual)
27 | {
28 | SaveFileDialog saveFileDialog = new SaveFileDialog
29 | {
30 | DefaultExt = ".xps",
31 | Filter = "XPS Documents (.xps)|*.xps"
32 | };
33 |
34 | bool? result = saveFileDialog.ShowDialog();
35 |
36 | if (result != true) return;
37 |
38 | XpsDocument xpsDocument = new XpsDocument(saveFileDialog.FileName, FileAccess.Write);
39 | XpsDocumentWriter xpsDocumentWriter = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
40 |
41 | xpsDocumentWriter.Write(visual);
42 | xpsDocument.Close();
43 | }
44 |
45 | public static void ExportVisualAsPdf(Visual visual)
46 | {
47 | SaveFileDialog saveFileDialog = new SaveFileDialog
48 | {
49 | DefaultExt = ".pdf",
50 | Filter = "PDF Documents (.pdf)|*.pdf"
51 | };
52 |
53 | bool? result = saveFileDialog.ShowDialog();
54 |
55 | if (result != true) return;
56 |
57 | using (MemoryStream memoryStream = new MemoryStream())
58 | {
59 | System.IO.Packaging.Package package = System.IO.Packaging.Package.Open(memoryStream, FileMode.Create);
60 | XpsDocument xpsDocument = new XpsDocument(package);
61 | XpsDocumentWriter xpsDocumentWriter = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
62 |
63 | xpsDocumentWriter.Write(visual);
64 | xpsDocument.Close();
65 | package.Close();
66 |
67 | var pdfXpsDoc = PdfSharp.Xps.XpsModel.XpsDocument.Open(memoryStream);
68 | XpsConverter.Convert(pdfXpsDoc, saveFileDialog.FileName, 0);
69 | }
70 | }
71 |
72 | private static readonly Lazy dpiX = new Lazy(() =>
73 | {
74 | PropertyInfo dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", BindingFlags.NonPublic | BindingFlags.Static);
75 | return (int)dpiXProperty.GetValue(null, null);
76 | });
77 |
78 | private static readonly Lazy dpiY = new Lazy(() =>
79 | {
80 | var dpiYProperty = typeof(SystemParameters).GetProperty("Dpi", BindingFlags.NonPublic | BindingFlags.Static);
81 | return (int)dpiYProperty.GetValue(null, null);
82 | });
83 |
84 | private static void SaveFrameworkElementAsImage(FrameworkElement element, string filePath, BitmapEncoder bitmapEncoder)
85 | {
86 | RenderTargetBitmap bitmap = new RenderTargetBitmap(
87 | pixelWidth: Convert.ToInt32((element.ActualWidth / DIUPerInch) * dpiX.Value),
88 | pixelHeight: Convert.ToInt32((element.ActualHeight / DIUPerInch) * dpiY.Value),
89 | dpiX: dpiX.Value,
90 | dpiY: dpiY.Value,
91 | pixelFormat: PixelFormats.Pbgra32);
92 | bitmap.Render(element);
93 |
94 | bitmapEncoder.Frames.Add(BitmapFrame.Create(bitmap));
95 | using (Stream fs = File.Create(filePath))
96 | {
97 | bitmapEncoder.Save(fs);
98 | }
99 | }
100 |
101 | public static void ExportFrameworkElementAsJpg(FrameworkElement element)
102 | {
103 | SaveFileDialog saveFileDialog = new SaveFileDialog
104 | {
105 | DefaultExt = ".jpg",
106 | Filter = "JPG Images (.jpg)|*.jpg"
107 | };
108 |
109 | bool? result = saveFileDialog.ShowDialog();
110 |
111 | if (result != true) return;
112 |
113 | SaveFrameworkElementAsImage(element, saveFileDialog.FileName, new JpegBitmapEncoder {QualityLevel = 100});
114 | }
115 |
116 | public static void ExportFrameworkElementAsBmp(FrameworkElement element)
117 | {
118 | SaveFileDialog saveFileDialog = new SaveFileDialog
119 | {
120 | DefaultExt = ".bmp",
121 | Filter = "BMP Images (.bmp)|*.bmp"
122 | };
123 |
124 | bool? result = saveFileDialog.ShowDialog();
125 |
126 | if (result != true) return;
127 |
128 | SaveFrameworkElementAsImage(element, saveFileDialog.FileName, new BmpBitmapEncoder());
129 | }
130 |
131 | public static void ExportFrameworkElementAsPng(FrameworkElement element)
132 | {
133 | SaveFileDialog saveFileDialog = new SaveFileDialog
134 | {
135 | DefaultExt = ".png",
136 | Filter = "PNG Images (.png)|*.png"
137 | };
138 |
139 | bool? result = saveFileDialog.ShowDialog();
140 |
141 | if (result != true) return;
142 |
143 | SaveFrameworkElementAsImage(element, saveFileDialog.FileName, new PngBitmapEncoder());
144 | }
145 |
146 | ///
147 | /// Divides elements of reportContainer into pages and prints them
148 | ///
149 | /// StackPanel containing report elements
150 | /// Data Context used in the report
151 | /// Margin of a report page
152 | /// Landscape or Portrait orientation
153 | /// Resources used in report
154 | /// Brush that will be used as background for report page
155 | ///
156 | /// Optional header for each page
157 | /// Note: You can use DynamicResource PageNumber in this template to display page number
158 | ///
159 | /// Use header only on the first page (default is false)
160 | ///
161 | /// Optional footer for each page
162 | /// Note: You can use DynamicResource PageNumber in this template to display page number
163 | ///
164 | /// Do not use footer on the first page (default is false)
165 | public static void PrintReport(
166 | StackPanel reportContainer,
167 | object dataContext,
168 | Thickness margin,
169 | ReportOrientation orientation,
170 | ResourceDictionary resourceDictionary = null,
171 | Brush backgroundBrush = null,
172 | DataTemplate reportHeaderDataTemplate = null,
173 | bool headerOnlyOnTheFirstPage = false,
174 | DataTemplate reportFooterDataTemplate = null,
175 | bool footerStartsFromTheSecondPage = false)
176 | {
177 | PrintDialog printDialog = new PrintDialog();
178 |
179 | bool? result = printDialog.ShowDialog();
180 |
181 | if (result != true) return;
182 |
183 | Size reportSize = GetReportSize(reportContainer, margin, orientation, printDialog);
184 |
185 | List ReportElements = new List(reportContainer.Children.Cast());
186 | reportContainer.Children.Clear(); //to avoid exception "Specified element is already the logical child of another element."
187 |
188 | List ReportPages =
189 | GetReportPages(
190 | resourceDictionary,
191 | backgroundBrush,
192 | ReportElements,
193 | dataContext,
194 | margin,
195 | reportSize,
196 | reportHeaderDataTemplate,
197 | headerOnlyOnTheFirstPage,
198 | reportFooterDataTemplate,
199 | footerStartsFromTheSecondPage);
200 |
201 | try
202 | {
203 | ReportPages.ForEach(reportPage => reportPage.Scale(reportSize, printDialog));
204 | ReportPages.ForEach((reportPage, index) => printDialog.PrintVisual(reportPage.LayoutRoot, $"Карточка Точки {index + 1}"));
205 | }
206 | finally
207 | {
208 | ReportPages.ForEach(reportPage => reportPage.ClearChildren());
209 | ReportElements.ForEach(elm => reportContainer.Children.Add(elm));
210 | reportContainer.UpdateLayout();
211 | }
212 | }
213 |
214 | ///
215 | /// Divides elements of reportContainer into pages and prints them
216 | /// NOTE: this overload uses default margin of 25
217 | ///
218 | /// StackPanel containing report elements
219 | /// Data Context used in the report
220 | /// Landscape or Portrait orientation
221 | /// Resources used in report
222 | /// Brush that will be used as background for report page
223 | ///
224 | /// Optional header for each page
225 | /// Note: You can use DynamicResource PageNumber in this template to display page number
226 | ///
227 | /// Use header only on the first page (default is false)
228 | ///
229 | /// Optional footer for each page
230 | /// Note: You can use DynamicResource PageNumber in this template to display page number
231 | ///
232 | /// Do not use footer on the first page (default is false)
233 | public static void PrintReport(
234 | StackPanel reportContainer,
235 | object dataContext,
236 | ReportOrientation orientation,
237 | ResourceDictionary resourceDictionary = null,
238 | Brush backgroundBrush = null,
239 | DataTemplate reportHeaderDataTemplate = null,
240 | bool headerOnlyOnTheFirstPage = false,
241 | DataTemplate reportFooterDataTemplate = null,
242 | bool footerStartsFromTheSecondPage = false)
243 | {
244 | PrintReport(
245 | reportContainer,
246 | dataContext,
247 | defaultMargin,
248 | orientation,
249 | resourceDictionary,
250 | backgroundBrush,
251 | reportHeaderDataTemplate,
252 | headerOnlyOnTheFirstPage,
253 | reportFooterDataTemplate,
254 | footerStartsFromTheSecondPage);
255 | }
256 |
257 | ///
258 | /// Divides elements of reportContainer into pages and exports them as PDF
259 | ///
260 | /// StackPanel containing report elements
261 | /// Data Context used in the report
262 | /// Margin of a report page
263 | /// Landscape or Portrait orientation
264 | /// Resources used in report
265 | /// Brush that will be used as background for report page
266 | ///
267 | /// Optional header for each page
268 | /// Note: You can use DynamicResource PageNumber in this template to display page number
269 | ///
270 | /// Use header only on the first page (default is false)
271 | ///
272 | /// Optional footer for each page
273 | /// Note: You can use DynamicResource PageNumber in this template to display page number
274 | ///
275 | /// Do not use footer on the first page (default is false)
276 | public static void ExportReportAsPdf(
277 | StackPanel reportContainer,
278 | object dataContext,
279 | Thickness margin,
280 | ReportOrientation orientation,
281 | ResourceDictionary resourceDictionary = null,
282 | Brush backgroundBrush = null,
283 | DataTemplate reportHeaderDataTemplate = null,
284 | bool headerOnlyOnTheFirstPage = false,
285 | DataTemplate reportFooterDataTemplate = null,
286 | bool footerStartsFromTheSecondPage = false)
287 | {
288 | SaveFileDialog saveFileDialog = new SaveFileDialog
289 | {
290 | DefaultExt = ".pdf",
291 | Filter = "PDF Documents (.pdf)|*.pdf"
292 | };
293 |
294 | bool? result = saveFileDialog.ShowDialog();
295 |
296 | if (result != true) return;
297 |
298 | Size reportSize = GetReportSize(reportContainer, margin, orientation);
299 |
300 | List ReportElements = new List(reportContainer.Children.Cast());
301 | reportContainer.Children.Clear(); //to avoid exception "Specified element is already the logical child of another element."
302 |
303 | List ReportPages =
304 | GetReportPages(
305 | resourceDictionary,
306 | backgroundBrush,
307 | ReportElements,
308 | dataContext,
309 | margin,
310 | reportSize,
311 | reportHeaderDataTemplate,
312 | headerOnlyOnTheFirstPage,
313 | reportFooterDataTemplate,
314 | footerStartsFromTheSecondPage);
315 |
316 | FixedDocument fixedDocument = new FixedDocument();
317 |
318 | try
319 | {
320 | using (MemoryStream memoryStream = new MemoryStream())
321 | {
322 | System.IO.Packaging.Package package = System.IO.Packaging.Package.Open(memoryStream, FileMode.Create);
323 | XpsDocument xpsDocument = new XpsDocument(package);
324 | XpsDocumentWriter xpsDocumentWriter = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
325 |
326 | foreach (Grid reportPage in ReportPages.Select(reportPage => reportPage.LayoutRoot))
327 | {
328 | reportPage.Width = reportPage.ActualWidth;
329 | reportPage.Height = reportPage.ActualHeight;
330 |
331 | FixedPage newFixedPage = new FixedPage();
332 | newFixedPage.Children.Add(reportPage);
333 | newFixedPage.Measure(reportSize);
334 | newFixedPage.Arrange(new Rect(reportSize));
335 | newFixedPage.Width = newFixedPage.ActualWidth;
336 | newFixedPage.Height = newFixedPage.ActualHeight;
337 | newFixedPage.Background = backgroundBrush;
338 | newFixedPage.UpdateLayout();
339 |
340 | PageContent pageContent = new PageContent();
341 | ((IAddChild)pageContent).AddChild(newFixedPage);
342 |
343 | fixedDocument.Pages.Add(pageContent);
344 | }
345 |
346 | xpsDocumentWriter.Write(fixedDocument);
347 | xpsDocument.Close();
348 | package.Close();
349 |
350 | var pdfXpsDoc = PdfSharp.Xps.XpsModel.XpsDocument.Open(memoryStream);
351 | XpsConverter.Convert(pdfXpsDoc, saveFileDialog.FileName, 0);
352 | }
353 | }
354 | finally
355 | {
356 | ReportPages.ForEach(reportPage => reportPage.ClearChildren());
357 | ReportElements.ForEach(elm => reportContainer.Children.Add(elm));
358 | reportContainer.UpdateLayout();
359 | }
360 | }
361 |
362 | ///
363 | /// Divides elements of reportContainer into pages and exports them as PDF
364 | /// NOTE: this overload uses default margin of 25
365 | ///
366 | /// StackPanel containing report elements
367 | /// Data Context used in the report
368 | /// Landscape or Portrait orientation
369 | /// Resources used in report
370 | /// Brush that will be used as background for report page
371 | ///
372 | /// Optional header for each page
373 | /// Note: You can use DynamicResource PageNumber in this template to display page number
374 | ///
375 | /// Use header only on the first page (default is false)
376 | ///
377 | /// Optional footer for each page
378 | /// Note: You can use DynamicResource PageNumber in this template to display page number
379 | ///
380 | /// Do not use footer on the first page (default is false)
381 | public static void ExportReportAsPdf(
382 | StackPanel reportContainer,
383 | object dataContext,
384 | ReportOrientation orientation,
385 | ResourceDictionary resourceDictionary = null,
386 | Brush backgroundBrush = null,
387 | DataTemplate reportHeaderDataTemplate = null,
388 | bool headerOnlyOnTheFirstPage = false,
389 | DataTemplate reportFooterDataTemplate = null,
390 | bool footerStartsFromTheSecondPage = false)
391 | {
392 | ExportReportAsPdf(
393 | reportContainer,
394 | dataContext,
395 | defaultMargin,
396 | orientation,
397 | resourceDictionary,
398 | backgroundBrush,
399 | reportHeaderDataTemplate,
400 | headerOnlyOnTheFirstPage,
401 | reportFooterDataTemplate,
402 | footerStartsFromTheSecondPage);
403 | }
404 |
405 | private static List GetReportPages(
406 | ResourceDictionary resourceDictionary,
407 | Brush backgroundBrush,
408 | List ReportElements,
409 | object dataContext,
410 | Thickness margin,
411 | Size reportSize,
412 | DataTemplate reportHeaderDataTemplate,
413 | bool headerOnlyOnTheFirstPage,
414 | DataTemplate reportFooterDataTemplate,
415 | bool footerStartsFromTheSecondPage)
416 | {
417 | int pageNumber = 1;
418 |
419 | List ReportPages =
420 | new List
421 | {
422 | new ReportPage(
423 | reportSize,
424 | backgroundBrush,
425 | margin,
426 | dataContext,
427 | resourceDictionary,
428 | reportHeaderDataTemplate,
429 | (footerStartsFromTheSecondPage) ? null : reportFooterDataTemplate,
430 | pageNumber)
431 | };
432 |
433 | foreach (FrameworkElement reportVisualElement in ReportElements)
434 | {
435 | if (ReportPages.Last().GetChildrenActualHeight() + GetActualHeightPlusMargin(reportVisualElement) > reportSize.Height - margin.Top - margin.Bottom)
436 | {
437 | pageNumber++;
438 |
439 | ReportPages.Add(
440 | new ReportPage(
441 | reportSize,
442 | backgroundBrush,
443 | margin,
444 | dataContext,
445 | resourceDictionary,
446 | (headerOnlyOnTheFirstPage) ? null : reportHeaderDataTemplate,
447 | reportFooterDataTemplate,
448 | pageNumber));
449 | }
450 |
451 | ReportPages.Last().AddElement(reportVisualElement);
452 | }
453 |
454 | foreach (ReportPage reportPage in ReportPages)
455 | {
456 | reportPage.LayoutRoot.Measure(reportSize);
457 | reportPage.LayoutRoot.Arrange(new Rect(reportSize));
458 | reportPage.LayoutRoot.UpdateLayout();
459 | }
460 |
461 | return ReportPages;
462 | }
463 |
464 | private static double GetActualHeightPlusMargin(FrameworkElement elm)
465 | {
466 | return elm.ActualHeight + elm.Margin.Top + elm.Margin.Bottom;
467 | }
468 |
469 | private static Size GetReportSize(StackPanel reportContainer, Thickness margin, ReportOrientation orientation, PrintDialog printDialog = null)
470 | {
471 | if (printDialog == null)
472 | printDialog = new PrintDialog();
473 |
474 | double reportWidth = reportContainer.ActualWidth + margin.Left + margin.Right;
475 |
476 | if (orientation == ReportOrientation.Landscape)
477 | printDialog.PrintTicket.PageOrientation = PageOrientation.Landscape;
478 |
479 | double reportHeight = (reportWidth / printDialog.PrintableAreaWidth) * printDialog.PrintableAreaHeight;
480 |
481 | return new Size(reportWidth, reportHeight);
482 | }
483 |
484 | private static void ForEach(this IEnumerable enumeration, Action action)
485 | {
486 | if (enumeration == null)
487 | return;
488 |
489 | int index = 0;
490 | foreach (T item in enumeration)
491 | {
492 | action(item, index);
493 | index++;
494 | }
495 | }
496 | }
497 | }
498 | #pragma warning restore 1591
499 |
500 |
501 |
--------------------------------------------------------------------------------
/SimpleWPFReporting/ReportOrientation.cs:
--------------------------------------------------------------------------------
1 | namespace SimpleWPFReporting
2 | {
3 | #pragma warning disable 1591
4 | public enum ReportOrientation
5 | {
6 | Portrait,
7 | Landscape
8 | }
9 |
10 | #pragma warning restore 1591
11 | }
12 |
--------------------------------------------------------------------------------
/SimpleWPFReporting/ReportPage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Windows;
4 | using System.Windows.Controls;
5 | using System.Windows.Media;
6 |
7 | namespace SimpleWPFReporting
8 | {
9 | internal class ReportPage
10 | {
11 | private readonly Grid layoutRootGrid;
12 | private readonly StackPanel stackPanel = new StackPanel();
13 | private readonly FrameworkElement reportHeader;
14 | private readonly FrameworkElement reportFooter;
15 |
16 | internal ReportPage(
17 | Size reportSize,
18 | Brush backgroundBrush,
19 | Thickness margin,
20 | object dataContext,
21 | ResourceDictionary resourceDictionary,
22 | DataTemplate reportHeaderDataTemplate,
23 | DataTemplate reportFooterDataTemplate,
24 | int pageNumber)
25 | {
26 | layoutRootGrid = new Grid
27 | {
28 | Margin = margin,
29 | Background = backgroundBrush,
30 | Resources = resourceDictionary,
31 | DataContext = dataContext,
32 | HorizontalAlignment = HorizontalAlignment.Stretch,
33 | VerticalAlignment = VerticalAlignment.Stretch
34 | };
35 |
36 |
37 | if (reportHeaderDataTemplate != null)
38 | {
39 | reportHeader = reportHeaderDataTemplate.LoadContent() as FrameworkElement;
40 |
41 | if (reportHeader == null)
42 | throw new Exception($"Couldn't cast content of {nameof(reportHeaderDataTemplate)} to {nameof(FrameworkElement)}");
43 |
44 | layoutRootGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
45 | Grid.SetRow(reportHeader, 0);
46 |
47 | AddPageNumberResource(reportHeader, pageNumber);
48 | layoutRootGrid.Children.Add(reportHeader);
49 | }
50 |
51 | layoutRootGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
52 | Grid.SetRow(stackPanel, reportHeader == null ? 0 : 1);
53 | layoutRootGrid.Children.Add(stackPanel);
54 |
55 | if (reportFooterDataTemplate != null)
56 | {
57 | reportFooter = reportFooterDataTemplate.LoadContent() as FrameworkElement;
58 |
59 | if (reportFooter == null)
60 | throw new Exception($"Couldn't cast content of {nameof(reportFooterDataTemplate)} to {nameof(FrameworkElement)}");
61 |
62 | layoutRootGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
63 | Grid.SetRow(reportFooter, reportHeader == null ? 1 : 2);
64 |
65 | AddPageNumberResource(reportFooter, pageNumber);
66 | layoutRootGrid.Children.Add(reportFooter);
67 | }
68 |
69 | layoutRootGrid.Measure(reportSize);
70 | layoutRootGrid.Arrange(new Rect(reportSize));
71 | layoutRootGrid.UpdateLayout();
72 | }
73 |
74 | internal void AddElement(UIElement element) => stackPanel.Children.Add(element);
75 |
76 | internal UIElementCollection Children => layoutRootGrid.Children;
77 |
78 | private static void AddPageNumberResource(FrameworkElement element, int pageNumber)
79 | {
80 | if (element.Resources.Contains("PageNumber"))
81 | element.Resources["PageNumber"] = pageNumber.ToString();
82 | else
83 | element.Resources.Add("PageNumber", pageNumber.ToString());
84 | }
85 |
86 | internal Grid LayoutRoot => layoutRootGrid;
87 |
88 | internal void ClearChildren()
89 | {
90 | stackPanel.Children.Clear();
91 | layoutRootGrid.Children.Clear();
92 | }
93 |
94 | internal double GetChildrenActualHeight()
95 | {
96 | double childrenActualHeight =
97 | stackPanel.Children
98 | .Cast()
99 | .Sum(elm => GetActualHeightPlusMargin(elm));
100 |
101 | if (reportHeader != null)
102 | childrenActualHeight += GetActualHeightPlusMargin(reportHeader);
103 |
104 | if (reportFooter != null)
105 | childrenActualHeight += GetActualHeightPlusMargin(reportFooter);
106 |
107 | return childrenActualHeight;
108 | }
109 |
110 | private static double GetActualHeightPlusMargin(FrameworkElement elm)
111 | {
112 | return elm.ActualHeight + elm.Margin.Top + elm.Margin.Bottom;
113 | }
114 |
115 | internal void Scale(Size reportSize, PrintDialog printDialog)
116 | {
117 | double reportScale = GetReportScale(reportSize, printDialog);
118 | layoutRootGrid.LayoutTransform = new ScaleTransform(reportScale, reportScale);
119 | Size scaledReportSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
120 |
121 | layoutRootGrid.Measure(scaledReportSize);
122 | layoutRootGrid.Arrange(new Rect(scaledReportSize));
123 | layoutRootGrid.UpdateLayout();
124 | }
125 |
126 | private static double GetReportScale(Size reportSize, PrintDialog printDialog)
127 | {
128 | if (reportSize.Width < printDialog.PrintableAreaWidth && reportSize.Height < printDialog.PrintableAreaHeight)
129 | return 1;
130 |
131 | double scale = Math.Min(printDialog.PrintableAreaWidth / reportSize.Width, printDialog.PrintableAreaHeight / reportSize.Height);
132 |
133 | scale = scale - 0.015; // To make sure the report will fit to the page
134 |
135 | return scale;
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/SimpleWPFReporting/SimpleWPFReporting.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {9846B28B-D22B-4B99-BFC1-420535730CE4}
8 | Library
9 | Properties
10 | SimpleWPFReporting
11 | SimpleWPFReporting
12 | v4.5
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 | bin\Debug\SimpleWPFReporting.XML
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 | bin\Release\SimpleWPFReporting.XML
34 |
35 |
36 |
37 | ..\packages\PdfSharp-WPF.SimpleWPFReporting.1.0.0\lib\net45\PdfSharp-WPF.dll
38 | True
39 |
40 |
41 | ..\packages\PdfSharp.Xps.SimpleWPFReporting.1.0.0\lib\net45\PdfSharp.Xps.dll
42 | True
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 |
76 |
--------------------------------------------------------------------------------
/SimpleWPFReporting/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------