├── .gitattributes
├── .gitignore
├── LICENSE
├── NPOI.Extension.sln
├── README.md
├── images
└── demo.PNG
├── samples
├── NPOI.Extension.Example.csproj
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── Report.cs
└── src
├── Attributes
├── ColumnAttribute.cs
├── FilterAttribute.cs
├── FreezeAttribute.cs
└── StatisticsAttribute.cs
├── Excel.cs
├── ExcelSetting.cs
├── Extensions
├── IEnumerableNpoiExtensions.cs
└── TypeExtensions.cs
├── FluentConfiguration
├── FluentConfiguration.cs
├── IFluentConfiguration.cs
└── PropertyConfiguration.cs
├── Internal
├── CellConfig.cs
├── FilterConfig.cs
├── FreezeConfig.cs
└── StatisticsConfig.cs
├── NPOI.Extension.csproj
├── NPOI.Extension.nuspec
├── Properties
└── AssemblyInfo.cs
└── 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 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studo 2015 cache/options directory
26 | .vs/
27 |
28 | # MSTest test Results
29 | [Tt]est[Rr]esult*/
30 | [Bb]uild[Ll]og.*
31 |
32 | # NUNIT
33 | *.VisualState.xml
34 | TestResult.xml
35 |
36 | # Build Results of an ATL Project
37 | [Dd]ebugPS/
38 | [Rr]eleasePS/
39 | dlldata.c
40 |
41 | *_i.c
42 | *_p.c
43 | *_i.h
44 | *.ilk
45 | *.meta
46 | *.obj
47 | *.pch
48 | *.pdb
49 | *.pgc
50 | *.pgd
51 | *.rsp
52 | *.sbr
53 | *.tlb
54 | *.tli
55 | *.tlh
56 | *.tmp
57 | *.tmp_proj
58 | *.log
59 | *.vspscc
60 | *.vssscc
61 | .builds
62 | *.pidb
63 | *.svclog
64 | *.scc
65 |
66 | # Chutzpah Test files
67 | _Chutzpah*
68 |
69 | # Visual C++ cache files
70 | ipch/
71 | *.aps
72 | *.ncb
73 | *.opensdf
74 | *.sdf
75 | *.cachefile
76 |
77 | # Visual Studio profiler
78 | *.psess
79 | *.vsp
80 | *.vspx
81 |
82 | # TFS 2012 Local Workspace
83 | $tf/
84 |
85 | # Guidance Automation Toolkit
86 | *.gpState
87 |
88 | # ReSharper is a .NET coding add-in
89 | _ReSharper*/
90 | *.[Rr]e[Ss]harper
91 | *.DotSettings.user
92 |
93 | # JustCode is a .NET coding addin-in
94 | .JustCode
95 |
96 | # TeamCity is a build add-in
97 | _TeamCity*
98 |
99 | # DotCover is a Code Coverage Tool
100 | *.dotCover
101 |
102 | # NCrunch
103 | _NCrunch_*
104 | .*crunch*.local.xml
105 |
106 | # MightyMoose
107 | *.mm.*
108 | AutoTest.Net/
109 |
110 | # Web workbench (sass)
111 | .sass-cache/
112 |
113 | # Installshield output folder
114 | [Ee]xpress/
115 |
116 | # DocProject is a documentation generator add-in
117 | DocProject/buildhelp/
118 | DocProject/Help/*.HxT
119 | DocProject/Help/*.HxC
120 | DocProject/Help/*.hhc
121 | DocProject/Help/*.hhk
122 | DocProject/Help/*.hhp
123 | DocProject/Help/Html2
124 | DocProject/Help/html
125 |
126 | # Click-Once directory
127 | publish/
128 |
129 | # Publish Web Output
130 | *.[Pp]ublish.xml
131 | *.azurePubxml
132 | # TODO: Comment the next line if you want to checkin your web deploy settings
133 | # but database connection strings (with potential passwords) will be unencrypted
134 | *.pubxml
135 | *.publishproj
136 |
137 | # NuGet Packages
138 | *.nupkg
139 | # The packages folder can be ignored because of Package Restore
140 | **/packages/*
141 | # except build/, which is used as an MSBuild target.
142 | !**/packages/build/
143 | # Uncomment if necessary however generally it will be regenerated when needed
144 | #!**/packages/repositories.config
145 |
146 | # Windows Azure Build Output
147 | csx/
148 | *.build.csdef
149 |
150 | # Windows Store app package directory
151 | AppPackages/
152 |
153 | # Others
154 | *.[Cc]ache
155 | ClientBin/
156 | [Ss]tyle[Cc]op.*
157 | ~$*
158 | *~
159 | *.dbmdl
160 | *.dbproj.schemaview
161 | *.pfx
162 | *.publishsettings
163 | node_modules/
164 | bower_components/
165 |
166 | # RIA/Silverlight projects
167 | Generated_Code/
168 |
169 | # Backup & report files from converting an old project file
170 | # to a newer Visual Studio version. Backup files are not needed,
171 | # because we have git ;-)
172 | _UpgradeReport_Files/
173 | Backup*/
174 | UpgradeLog*.XML
175 | UpgradeLog*.htm
176 |
177 | # SQL Server files
178 | *.mdf
179 | *.ldf
180 |
181 | # Business Intelligence projects
182 | *.rdl.data
183 | *.bim.layout
184 | *.bim_*.settings
185 |
186 | # Microsoft Fakes
187 | FakesAssemblies/
188 |
189 | # Node.js Tools for Visual Studio
190 | .ntvs_analysis.dat
191 |
192 | # Visual Studio 6 build log
193 | *.plg
194 |
195 | # Visual Studio 6 workspace options file
196 | *.opt
197 | *.xml
198 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/NPOI.Extension.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8016EBDF-3080-41D2-8864-756493FAE3D8}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{E05FA5CC-E386-42EB-BFD3-A72FE5E8F00F}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPOI.Extension", "src\NPOI.Extension.csproj", "{5ADDD29D-B3AF-4966-B730-5E5192D0E9DF}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NPOI.Extension.Example", "samples\NPOI.Extension.Example.csproj", "{08DCA8C6-8AB5-4341-AB82-001EC59B9ACC}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8BF2A404-4265-4765-89EA-E0642F142C4B}"
15 | ProjectSection(SolutionItems) = preProject
16 | README.md = README.md
17 | EndProjectSection
18 | EndProject
19 | Global
20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
21 | Debug|Any CPU = Debug|Any CPU
22 | Release|Any CPU = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
25 | {5ADDD29D-B3AF-4966-B730-5E5192D0E9DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {5ADDD29D-B3AF-4966-B730-5E5192D0E9DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {5ADDD29D-B3AF-4966-B730-5E5192D0E9DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {5ADDD29D-B3AF-4966-B730-5E5192D0E9DF}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {08DCA8C6-8AB5-4341-AB82-001EC59B9ACC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {08DCA8C6-8AB5-4341-AB82-001EC59B9ACC}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {08DCA8C6-8AB5-4341-AB82-001EC59B9ACC}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {08DCA8C6-8AB5-4341-AB82-001EC59B9ACC}.Release|Any CPU.Build.0 = Release|Any CPU
33 | EndGlobalSection
34 | GlobalSection(SolutionProperties) = preSolution
35 | HideSolutionNode = FALSE
36 | EndGlobalSection
37 | GlobalSection(NestedProjects) = preSolution
38 | {5ADDD29D-B3AF-4966-B730-5E5192D0E9DF} = {8016EBDF-3080-41D2-8864-756493FAE3D8}
39 | {08DCA8C6-8AB5-4341-AB82-001EC59B9ACC} = {E05FA5CC-E386-42EB-BFD3-A72FE5E8F00F}
40 | EndGlobalSection
41 | EndGlobal
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | The extensions for the NPOI, which provides IEnumerable<T> have save to and load from excel functionalities.
2 |
3 | # IMPORTAMT
4 | 1. The future features will be support by [FluentExcel](https://github.com/Arch/FluentExcel), and will only support `Fluent API`.
5 | 2. All the issues found in this repo will be and only be fixed by [FluentExcel](https://github.com/Arch/FluentExcel), so, please update your code to use `FluentExcel`.
6 |
7 | # Features
8 | - [x] Decouple the configuration from the POCO model by using `fluent api`.
9 | - [x] Support attributes based configurations.
10 | - [x] Support none configuration POCO, so that if English is your mother langurage, none any more configurations;
11 |
12 | The first two features will be very useful for English not their mother language developers.
13 |
14 | # Overview
15 |
16 | 
17 |
18 | # Get Started
19 |
20 | The following demo codes come from [sample](samples), download and run it for more information.
21 |
22 | ## Using Package Manager Console to install NPOI.Extension
23 |
24 | PM> Install-Package NPOI.Extension
25 |
26 | ## Reference NPOI.Extension in code
27 |
28 | using NPOI.Extension;
29 |
30 | ## Configure model's excel behaviors
31 |
32 | We can use `fluent api` or `attributes` to configure the model excel behaviors. If both had been used, `fluent` configurations will has the `Hight Priority`
33 |
34 | ### 1. Use Fluent Api
35 |
36 | ```csharp
37 | public class Report {
38 | public string City { get; set; }
39 | public string Building { get; set; }
40 | public DateTime HandleTime { get; set; }
41 | public string Broker { get; set; }
42 | public string Customer { get; set; }
43 | public string Room { get; set; }
44 | public decimal Brokerage { get; set; }
45 | public decimal Profits { get; set; }
46 | }
47 |
48 | ///
49 | /// Use fluent configuration api. (doesn't poison your POCO)
50 | ///
51 | static void FluentConfiguration()
52 | {
53 | var fc = Excel.Setting.For();
54 |
55 | fc.HasStatistics("合计", "SUM", 6, 7)
56 | .HasFilter(firstColumn: 0, lastColumn: 2, firstRow: 0)
57 | .HasFreeze(columnSplit: 2,rowSplit: 1, leftMostColumn: 2, topMostRow: 1);
58 |
59 | fc.Property(r => r.City)
60 | .HasExcelIndex(0)
61 | .HasExcelTitle("城市")
62 | .IsMergeEnabled();
63 |
64 | fc.Property(r => r.Building)
65 | .HasExcelIndex(1)
66 | .HasExcelTitle("楼盘")
67 | .IsMergeEnabled();
68 |
69 | fc.Property(r => r.HandleTime)
70 | .HasExcelIndex(2)
71 | .HasExcelTitle("成交时间")
72 | .HasDataFormatter("yyyy-MM-dd HH:mm:ss");
73 |
74 | fc.Property(r => r.Broker)
75 | .HasExcelIndex(3)
76 | .HasExcelTitle("经纪人");
77 |
78 | fc.Property(r => r.Customer)
79 | .HasExcelIndex(4)
80 | .HasExcelTitle("客户");
81 |
82 | fc.Property(r => r.Room)
83 | .HasExcelIndex(5)
84 | .HasExcelTitle("房源");
85 |
86 | fc.Property(r => r.Brokerage)
87 | .HasExcelIndex(6)
88 | .HasExcelTitle("佣金(元)");
89 |
90 | fc.Property(r => r.Profits)
91 | .HasExcelIndex(7)
92 | .HasExcelTitle("收益(元)");
93 | }
94 | ```
95 |
96 | ### 2. Use attributes
97 |
98 | ```csharp
99 | [Statistics(Name = "合计", Formula = "SUM", Columns = new[] { 6, 7 })]
100 | [Filter(FirstCol = 0, FirstRow = 0, LastCol = 2)]
101 | [Freeze(ColSplit = 2, RowSplit = 1, LeftMostColumn = 2, TopRow = 1)]
102 | public class Report {
103 | [Column(Index = 0, Title = "城市", AllowMerge = true)]
104 | public string City { get; set; }
105 | [Column(Index = 1, Title = "楼盘", AllowMerge = true)]
106 | public string Building { get; set; }
107 | [Column(Index = 2, Title = "成交时间", Formatter = "yyyy-MM-dd HH:mm:ss")]
108 | public DateTime HandleTime { get; set; }
109 | [Column(Index = 3, Title = "经纪人")]
110 | public string Broker { get; set; }
111 | [Column(Index = 4, Title = "客户")]
112 | public string Customer { get; set; }
113 | [Column(Index = 5, Title = "房源")]
114 | public string Room { get; set; }
115 | [Column(Index = 6, Title = "佣金(元)")]
116 | public decimal Brokerage { get; set; }
117 | [Column(Index = 7, Title = "收益(元)")]
118 | public decimal Profits { get; set; }
119 | }
120 | ```
121 |
122 | ## Export POCO to excel & Load IEnumerable<T> from excel.
123 |
124 | ```csharp
125 | using System;
126 | using NPOI.Extension;
127 |
128 | namespace samples
129 | {
130 | class Program
131 | {
132 | static void Main(string[] args)
133 | {
134 | // global call this
135 | FluentConfiguration();
136 |
137 | var len = 10;
138 | var reports = new Report[len];
139 | for (int i = 0; i < len; i++)
140 | {
141 | reports[i] = new Report
142 | {
143 | City = "ningbo",
144 | Building = "世茂首府",
145 | HandleTime = new DateTime(2015, 11, 23),
146 | Broker = "rigofunc 18957139**7",
147 | Customer = "rigofunc 18957139**7",
148 | Room = "2#1703",
149 | Brokerage = 125M,
150 | Profits = 25m
151 | };
152 | }
153 |
154 | var excelFile = @"/Users/rigofunc/Documents/sample.xlsx";
155 |
156 | // save to excel file
157 | reports.ToExcel(excelFile);
158 |
159 | // load from excel
160 | var loadFromExcel = Excel.Load(excelFile);
161 | }
162 |
163 | ///
164 | /// Use fluent configuration api. (doesn't poison your POCO)
165 | ///
166 | static void FluentConfiguration()
167 | {
168 | var fc = Excel.Setting.For();
169 |
170 | fc.HasStatistics("合计", "SUM", 6, 7)
171 | .HasFilter(firstColumn: 0, lastColumn: 2, firstRow: 0)
172 | .HasFreeze(columnSplit: 2,rowSplit: 1, leftMostColumn: 2, topMostRow: 1);
173 |
174 | fc.Property(r => r.City)
175 | .HasExcelIndex(0)
176 | .HasExcelTitle("城市")
177 | .IsMergeEnabled();
178 |
179 | fc.Property(r => r.Building)
180 | .HasExcelIndex(1)
181 | .HasExcelTitle("楼盘")
182 | .IsMergeEnabled();
183 |
184 | fc.Property(r => r.HandleTime)
185 | .HasExcelIndex(2)
186 | .HasExcelTitle("成交时间")
187 | .HasDataFormatter("yyyy-MM-dd");
188 |
189 | fc.Property(r => r.Broker)
190 | .HasExcelIndex(3)
191 | .HasExcelTitle("经纪人");
192 |
193 | fc.Property(r => r.Customer)
194 | .HasExcelIndex(4)
195 | .HasExcelTitle("客户");
196 |
197 | fc.Property(r => r.Room)
198 | .HasExcelIndex(5)
199 | .HasExcelTitle("房源");
200 |
201 | fc.Property(r => r.Brokerage)
202 | .HasExcelIndex(6)
203 | .HasExcelTitle("佣金(元)");
204 |
205 | fc.Property(r => r.Profits)
206 | .HasExcelIndex(7)
207 | .HasExcelTitle("收益(元)");
208 | }
209 | }
210 | }
211 | ```
212 |
--------------------------------------------------------------------------------
/images/demo.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rigofunc/NPOI.Extension/95446d0be0aab1b6bac53e540b54c7ce91f32041/images/demo.PNG
--------------------------------------------------------------------------------
/samples/NPOI.Extension.Example.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {08DCA8C6-8AB5-4341-AB82-001EC59B9ACC}
8 | Exe
9 | Properties
10 | samples
11 | samples
12 | v4.6
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {5addd29d-b3af-4966-b730-5e5192d0e9df}
47 | NPOI.Extension
48 |
49 |
50 |
51 |
58 |
--------------------------------------------------------------------------------
/samples/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using NPOI.Extension;
3 |
4 | namespace samples
5 | {
6 | class Program
7 | {
8 | static void Main(string[] args)
9 | {
10 | // global call this
11 | FluentConfiguration();
12 |
13 | var len = 10;
14 | var reports = new Report[len];
15 | for (int i = 0; i < len; i++)
16 | {
17 | reports[i] = new Report
18 | {
19 | City = "ningbo",
20 | Building = "世茂首府",
21 | HandleTime = new DateTime(2015, 11, 23),
22 | Broker = "rigofunc 18957139**7",
23 | Customer = "rigofunc 18957139**7",
24 | Room = "2#1703",
25 | Brokerage = 125M,
26 | Profits = 25m
27 | };
28 | }
29 |
30 | var excelFile = @"/Users/rigofunc/Documents/sample.xlsx";
31 |
32 | // save to excel file
33 | reports.ToExcel(excelFile);
34 |
35 | // load from excel
36 | var loadFromExcel = Excel.Load(excelFile);
37 | }
38 |
39 | ///
40 | /// Use fluent configuration api. (doesn't poison your POCO)
41 | ///
42 | static void FluentConfiguration()
43 | {
44 | var fc = Excel.Setting.For();
45 |
46 | fc.HasStatistics("合计", "SUM", 6, 7)
47 | .HasFilter(firstColumn: 0, lastColumn: 2, firstRow: 0)
48 | .HasFreeze(columnSplit: 2,rowSplit: 1, leftMostColumn: 2, topMostRow: 1);
49 |
50 | fc.Property(r => r.City)
51 | .HasExcelIndex(0)
52 | .HasExcelTitle("城市")
53 | .IsMergeEnabled();
54 |
55 | fc.Property(r => r.Building)
56 | .HasExcelIndex(1)
57 | .HasExcelTitle("楼盘")
58 | .IsMergeEnabled();
59 |
60 | fc.Property(r => r.HandleTime)
61 | .HasExcelIndex(2)
62 | .HasExcelTitle("成交时间")
63 | .HasDataFormatter("yyyy-MM-dd");
64 |
65 | fc.Property(r => r.Broker)
66 | .HasExcelIndex(3)
67 | .HasExcelTitle("经纪人");
68 |
69 | fc.Property(r => r.Customer)
70 | .HasExcelIndex(4)
71 | .HasExcelTitle("客户");
72 |
73 | fc.Property(r => r.Room)
74 | .HasExcelIndex(5)
75 | .HasExcelTitle("房源");
76 |
77 | fc.Property(r => r.Brokerage)
78 | .HasExcelIndex(6)
79 | .HasExcelTitle("佣金(元)");
80 |
81 | fc.Property(r => r.Profits)
82 | .HasExcelIndex(7)
83 | .HasExcelTitle("收益(元)");
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/samples/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("NPOI.Extension.Example")]
9 | [assembly: AssemblyDescription("The examples of the NPOI.Extension")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("RigoFunc (xuyingting)")]
12 | [assembly: AssemblyProduct("NPOI.Extension.Example")]
13 | [assembly: AssemblyCopyright("Copyright © RigoFunc (xuyingting). All rights reserved.")]
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("08dca8c6-8ab5-4341-ab82-001ec59b9acc")]
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 |
--------------------------------------------------------------------------------
/samples/Report.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using NPOI.Extension;
3 |
4 | namespace samples {
5 | [Statistics(Name = "合计", Formula = "SUM", Columns = new[] { 6, 7 })]
6 | [Filter(FirstCol = 0, FirstRow = 0, LastCol = 2)]
7 | [Freeze(ColSplit = 2, RowSplit = 1, LeftMostColumn = 2, TopRow = 1)]
8 | public class Report {
9 | [Column(Index = 0, Title = "城市", AllowMerge = true)]
10 | public string City { get; set; }
11 | [Column(Index = 1, Title = "楼盘", AllowMerge = true)]
12 | public string Building { get; set; }
13 | [Column(Index = 2, Title = "成交时间", Formatter = "yyyy-MM-dd HH:mm:ss")]
14 | public DateTime HandleTime { get; set; }
15 | [Column(Index = 3, Title = "经纪人")]
16 | public string Broker { get; set; }
17 | [Column(Index = 4, Title = "客户")]
18 | public string Customer { get; set; }
19 | [Column(Index = 5, Title = "房源")]
20 | public string Room { get; set; }
21 | [Column(Index = 6, Title = "佣金(元)")]
22 | public decimal Brokerage { get; set; }
23 | [Column(Index = 7, Title = "收益(元)")]
24 | public decimal Profits { get; set; }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Attributes/ColumnAttribute.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved
2 |
3 | namespace NPOI.Extension
4 | {
5 | using System;
6 |
7 | ///
8 | /// Represents a custom attribute to control object's properties to excel columns behaviors.
9 | ///
10 | [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
11 | public class ColumnAttribute : Attribute
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | public ColumnAttribute()
17 | {
18 | CellConfig = new CellConfig();
19 | }
20 |
21 | ///
22 | /// Gets or sets the title of the column.
23 | ///
24 | ///
25 | /// If the is null or empty, will use property name as the excel column title.
26 | ///
27 | public string Title
28 | {
29 | get
30 | {
31 | return CellConfig.Title;
32 | }
33 | set
34 | {
35 | CellConfig.Title = value;
36 | }
37 | }
38 |
39 | ///
40 | /// If was not set and AutoIndex is true NPOI.Extension will try to autodiscover the column index by its property.
41 | ///
42 | public bool AutoIndex
43 | {
44 | get
45 | {
46 | return CellConfig.AutoIndex;
47 | }
48 | set
49 | {
50 | CellConfig.AutoIndex = value;
51 | }
52 | }
53 |
54 | ///
55 | /// Gets or sets the column index.
56 | ///
57 | /// The index.
58 | public int Index
59 | {
60 | get
61 | {
62 | return CellConfig.Index;
63 | }
64 | set
65 | {
66 | CellConfig.Index = value;
67 | }
68 | }
69 |
70 | ///
71 | /// Gets or sets a value indicating whether allow merge the same value cells.
72 | ///
73 | public bool AllowMerge
74 | {
75 | get
76 | {
77 | return CellConfig.AllowMerge;
78 | }
79 | set
80 | {
81 | CellConfig.AllowMerge = value;
82 | }
83 | }
84 |
85 | ///
86 | /// Gets or sets a value indicating whether this value of the propery is ignored.
87 | ///
88 | /// true if is ignored; otherwise, false.
89 | public bool IsIgnored
90 | {
91 | get
92 | {
93 | return CellConfig.IsIgnored;
94 | }
95 | set
96 | {
97 | CellConfig.IsIgnored = value;
98 | }
99 | }
100 |
101 | ///
102 | /// Gets or sets the formatter for formatting the value.
103 | ///
104 | /// The format.
105 | public string Formatter
106 | {
107 | get
108 | {
109 | return CellConfig.Formatter;
110 | }
111 | set
112 | {
113 | CellConfig.Formatter = value;
114 | }
115 | }
116 |
117 | ///
118 | /// Gets the cell config.
119 | ///
120 | /// The cell config.
121 | internal CellConfig CellConfig { get; }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/Attributes/FilterAttribute.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | using System;
6 |
7 | ///
8 | /// Represents a custom attribute to control excel filter behaviors.
9 | ///
10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
11 | public class FilterAttribute : Attribute
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | public FilterAttribute()
17 | {
18 | FilterConfig = new FilterConfig();
19 | }
20 |
21 | ///
22 | /// Gets or sets the first row index.
23 | ///
24 | public int FirstRow
25 | {
26 | get
27 | {
28 | return FilterConfig.FirstRow;
29 | }
30 | set
31 | {
32 | FilterConfig.FirstRow = value;
33 | }
34 | }
35 |
36 | ///
37 | /// Gets or sets the last row index.
38 | ///
39 | ///
40 | /// If the is null, the value is dynamic calculate by code.
41 | ///
42 | public int? LastRow
43 | {
44 | get
45 | {
46 | return FilterConfig.LastRow;
47 | }
48 | set
49 | {
50 | FilterConfig.LastRow = value;
51 | }
52 | }
53 |
54 | ///
55 | /// Gets or sets the first column index.
56 | ///
57 | public int FirstCol
58 | {
59 | get
60 | {
61 | return FilterConfig.FirstCol;
62 | }
63 | set
64 | {
65 | FilterConfig.FirstCol = value;
66 | }
67 | }
68 |
69 | ///
70 | /// Gets or sets the last column index.
71 | ///
72 | public int LastCol
73 | {
74 | get
75 | {
76 | return FilterConfig.LastCol;
77 | }
78 | set
79 | {
80 | FilterConfig.LastCol = value;
81 | }
82 | }
83 |
84 | ///
85 | /// Gets or the filter config.
86 | ///
87 | /// The filter config.
88 | internal FilterConfig FilterConfig { get; }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/Attributes/FreezeAttribute.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | using System;
6 |
7 | ///
8 | /// Represents a custom attribute to control excel freeze behaviors.
9 | ///
10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
11 | public class FreezeAttribute : Attribute
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | public FreezeAttribute()
17 | {
18 | FreezeConfig = new FreezeConfig();
19 | }
20 |
21 | ///
22 | /// Gets or sets the column number to split.
23 | ///
24 | public int ColSplit
25 | {
26 | get
27 | {
28 | return FreezeConfig.ColSplit;
29 | }
30 | set
31 | {
32 | FreezeConfig.ColSplit = value;
33 | }
34 | }
35 |
36 | ///
37 | /// Gets or sets the row number to split.
38 | ///
39 | public int RowSplit
40 | {
41 | get
42 | {
43 | return FreezeConfig.RowSplit;
44 | }
45 | set
46 | {
47 | FreezeConfig.RowSplit = value;
48 | }
49 | }
50 |
51 | ///
52 | /// Gets or sets the left most culomn index.
53 | ///
54 | public int LeftMostColumn
55 | {
56 | get
57 | {
58 | return FreezeConfig.LeftMostColumn;
59 | }
60 | set
61 | {
62 | FreezeConfig.LeftMostColumn = value;
63 | }
64 | }
65 |
66 | ///
67 | /// Gets or sets the top most row index.
68 | ///
69 | public int TopRow
70 | {
71 | get
72 | {
73 | return FreezeConfig.TopRow;
74 | }
75 | set
76 | {
77 | FreezeConfig.TopRow = value;
78 | }
79 | }
80 |
81 | ///
82 | /// Gets the freeze config.
83 | ///
84 | /// The freeze config.
85 | internal FreezeConfig FreezeConfig { get; }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Attributes/StatisticsAttribute.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | using System;
6 |
7 | ///
8 | /// Represents a custom attribute for some simple statistics.
9 | ///
10 | ///
11 | /// Only for vertical, not for horizontal statistics. and in current version,
12 | /// doesn't allow apply multiple to one class.
13 | ///
14 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
15 | public class StatisticsAttribute : Attribute
16 | {
17 | ///
18 | /// Initializes a new instance of the class.
19 | ///
20 | public StatisticsAttribute()
21 | {
22 | StatisticsConfig = new StatisticsConfig();
23 | }
24 |
25 | ///
26 | /// Gets or sets the statistics name. (e.g. Total)
27 | ///
28 | ///
29 | /// In current version, the default name location is (last row, first cell)
30 | ///
31 | public string Name
32 | {
33 | get
34 | {
35 | return StatisticsConfig.Name;
36 | }
37 | set
38 | {
39 | StatisticsConfig.Name = value;
40 | }
41 | }
42 |
43 | ///
44 | /// Gets or sets the cell formula, such as SUM, AVERAGE and so on, which applyable for vertical statistics.
45 | ///
46 | public string Formula
47 | {
48 | get
49 | {
50 | return StatisticsConfig.Formula;
51 | }
52 | set
53 | {
54 | StatisticsConfig.Formula = value;
55 | }
56 | }
57 |
58 | ///
59 | /// Gets or sets the column indexes for statistics. if is SUM,
60 | /// and is [1,3], for example, the column No. 1 and 3 will be
61 | /// SUM for first row to last row.
62 | ///
63 | public int[] Columns
64 | {
65 | get
66 | {
67 | return StatisticsConfig.Columns;
68 | }
69 | set
70 | {
71 | StatisticsConfig.Columns = value;
72 | }
73 | }
74 |
75 | ///
76 | /// Gets the statistics config.
77 | ///
78 | /// The statistics config.
79 | internal StatisticsConfig StatisticsConfig { get; }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Excel.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | using System;
6 | using System.Linq;
7 | using System.IO;
8 | using System.Globalization;
9 | using System.Collections.Generic;
10 | using System.Reflection;
11 | using SS.UserModel;
12 | using HSSF.UserModel;
13 | using XSSF.UserModel;
14 |
15 | ///
16 | /// Represents the cell value converter, which convert the value to another value.
17 | ///
18 | /// The row of the excel sheet.
19 | /// The cell of the excel sheet.
20 | /// The value to convert.
21 | /// The converted value.
22 | public delegate object ValueConverter(int row, int cell, object value);
23 |
24 | ///
25 | /// Provides some methods for loading from excel.
26 | ///
27 | public static class Excel
28 | {
29 | ///
30 | /// Gets or sets the setting.
31 | ///
32 | /// The setting.
33 | public static ExcelSetting Setting { get; set; } = new ExcelSetting();
34 |
35 | ///
36 | /// Loading from specified excel file.
37 | /// ///
38 | /// The type of the model.
39 | /// The excel file.
40 | /// The row to start read.
41 | /// Which sheet to read.
42 | /// The cell value convert.
43 | /// The loading from excel.
44 | public static IEnumerable Load(string excelFile, int startRow = 1, int sheetIndex = 0, ValueConverter valueConverter = null) where T : class, new()
45 | {
46 | if (!File.Exists(excelFile))
47 | {
48 | throw new FileNotFoundException();
49 | }
50 |
51 | var workbook = InitializeWorkbook(excelFile);
52 |
53 | // currently, only handle sheet one (or call side using foreach to support multiple sheet)
54 | var sheet = workbook.GetSheetAt(sheetIndex);
55 |
56 | // get the writable properties
57 | var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty);
58 |
59 | bool fluentConfigEnabled = false;
60 | // get the fluent config
61 | if (Excel.Setting.FluentConfigs.TryGetValue(typeof(T), out var fluentConfig))
62 | {
63 | fluentConfigEnabled = true;
64 | }
65 |
66 | var cellConfigs = new CellConfig[properties.Length];
67 | for (var j = 0; j < properties.Length; j++)
68 | {
69 | var property = properties[j];
70 | if (fluentConfigEnabled && fluentConfig.PropertyConfigs.TryGetValue(property, out var pc))
71 | {
72 | // fluent configure first(Hight Priority)
73 | cellConfigs[j] = pc.CellConfig;
74 | }
75 | else
76 | {
77 | var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true) as ColumnAttribute[];
78 | if (attrs != null && attrs.Length > 0)
79 | {
80 | cellConfigs[j] = attrs[0].CellConfig;
81 | }
82 | else
83 | {
84 | cellConfigs[j] = null;
85 | }
86 | }
87 | }
88 |
89 | var statistics = new List();
90 | if (fluentConfigEnabled)
91 | {
92 | statistics.AddRange(fluentConfig.StatisticsConfigs);
93 | }
94 | else
95 | {
96 | var attributes = typeof(T).GetCustomAttributes(typeof(StatisticsAttribute), true) as StatisticsAttribute[];
97 | if (attributes != null && attributes.Length > 0)
98 | {
99 | foreach (var item in attributes)
100 | {
101 | statistics.Add(item.StatisticsConfig);
102 | }
103 | }
104 | }
105 |
106 | var list = new List();
107 | int idx = 0;
108 |
109 | IRow headerRow = null;
110 |
111 | // get the physical rows
112 | var rows = sheet.GetRowEnumerator();
113 | while (rows.MoveNext())
114 | {
115 | var row = rows.Current as IRow;
116 |
117 | if (idx == 0)
118 | headerRow = row;
119 | idx++;
120 |
121 | if (row.RowNum < startRow)
122 | {
123 | continue;
124 | }
125 |
126 | var item = new T();
127 | var itemIsValid = true;
128 | for (int i = 0; i < properties.Length; i++)
129 | {
130 | var prop = properties[i];
131 |
132 | int index = i;
133 | var config = cellConfigs[i];
134 | if (config != null)
135 | {
136 | index = config.Index;
137 |
138 | // Try to autodiscover index from title and cache
139 | if (index < 0 && config.AutoIndex && !string.IsNullOrEmpty(config.Title))
140 | {
141 | foreach (var cell in headerRow.Cells)
142 | {
143 | if (!string.IsNullOrEmpty(cell.StringCellValue))
144 | {
145 | if (cell.StringCellValue.Equals(config.Title, StringComparison.InvariantCultureIgnoreCase))
146 | {
147 | index = cell.ColumnIndex;
148 |
149 | // cache
150 | config.Index = index;
151 |
152 | break;
153 | }
154 | }
155 | }
156 | }
157 |
158 | // check again
159 | if (index < 0)
160 | {
161 | throw new ApplicationException("Please set the 'index' or 'autoIndex' by fluent api or attributes");
162 | }
163 | }
164 |
165 | var value = row.GetCellValue(index);
166 | if (valueConverter != null)
167 | {
168 | value = valueConverter(row.RowNum, index, value);
169 | }
170 |
171 | if (value == null)
172 | {
173 | continue;
174 | }
175 |
176 | // check whether is statics row
177 | if (idx > startRow + 1 && index == 0
178 | &&
179 | statistics.Any(s => s.Name.Equals(value.ToString(), StringComparison.InvariantCultureIgnoreCase)))
180 | {
181 | var st = statistics.FirstOrDefault(s => s.Name.Equals(value.ToString(), StringComparison.InvariantCultureIgnoreCase));
182 | var formula = row.GetCellValue(st.Columns.First()).ToString();
183 | if (formula.StartsWith(st.Formula, StringComparison.InvariantCultureIgnoreCase))
184 | {
185 | itemIsValid = false;
186 | break;
187 | }
188 | }
189 |
190 | // property type
191 | var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
192 |
193 | var safeValue = Convert.ChangeType(value, propType, CultureInfo.CurrentCulture);
194 |
195 | prop.SetValue(item, safeValue, null);
196 | }
197 |
198 | if (itemIsValid)
199 | {
200 | list.Add(item);
201 | }
202 | }
203 |
204 | return list;
205 | }
206 |
207 | internal static object GetCellValue(this IRow row, int index)
208 | {
209 | var cell = row.GetCell(index);
210 | if (cell == null)
211 | {
212 | return null;
213 | }
214 |
215 | if (cell.IsMergedCell)
216 | {
217 | // what can I do here?
218 |
219 | }
220 |
221 | switch (cell.CellType)
222 | {
223 | // This is a trick to get the correct value of the cell.
224 | // NumericCellValue will return a numeric value no matter the cell value is a date or a number.
225 | case CellType.Numeric:
226 | return cell.ToString();
227 | case CellType.String:
228 | return cell.StringCellValue;
229 | case CellType.Boolean:
230 | return cell.BooleanCellValue;
231 | case CellType.Error:
232 | return cell.ErrorCellValue;
233 |
234 | // how?
235 | case CellType.Formula:
236 | return cell.ToString();
237 |
238 | case CellType.Blank:
239 | case CellType.Unknown:
240 | default:
241 | return null;
242 | }
243 | }
244 |
245 | internal static object GetDefault(this Type type)
246 | {
247 | if (type.IsValueType)
248 | {
249 | return Activator.CreateInstance(type);
250 | }
251 |
252 | return null;
253 | }
254 |
255 | private static IWorkbook InitializeWorkbook(string excelFile)
256 | {
257 | if (Path.GetExtension(excelFile).Equals(".xls"))
258 | {
259 | using (var file = new FileStream(excelFile, FileMode.Open, FileAccess.Read))
260 | {
261 | return new HSSFWorkbook(file);
262 | }
263 | }
264 | else if (Path.GetExtension(excelFile).Equals(".xlsx"))
265 | {
266 | using (var file = new FileStream(excelFile, FileMode.Open, FileAccess.Read))
267 | {
268 | return new XSSFWorkbook(file);
269 | }
270 | }
271 | else
272 | {
273 | throw new NotSupportedException($"not an excel file {excelFile}");
274 | }
275 | }
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/src/ExcelSetting.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | using NPOI.HSSF.Util;
6 | using NPOI.SS.UserModel;
7 | using System;
8 | using System.Collections.Generic;
9 |
10 | ///
11 | /// Represents the all setting for save to and loading from excel.
12 | ///
13 | public class ExcelSetting
14 | {
15 | ///
16 | /// Gets or sets the comany name property of the generated excel file.
17 | ///
18 | public string Company { get; set; } = "rigofunc (yingtingxu)";
19 |
20 | ///
21 | /// Gets or sets the author property of the generated excel file.
22 | ///
23 | public string Author { get; set; } = "rigofunc (yingtingxu)";
24 |
25 | ///
26 | /// Gets or sets the subject property of the generated excel file.
27 | ///
28 | public string Subject { get; set; } = "The extensions of NPOI, which provides IEnumerable; save to and load from excel.";
29 |
30 | ///
31 | /// Gets or sets a value indicating whether to use *.xlsx file extension.
32 | ///
33 | public bool UserXlsx { get; set; } = true;
34 |
35 | ///
36 | /// Gets or sets the date time formatter.
37 | ///
38 | [Obsolete("This configuration doesn't work now, please using fluent api or attribute to configure this.", true)]
39 | public string DateFormatter { get; set; } = "yyyy-MM-dd HH:mm:ss";
40 |
41 | ///
42 | /// Gets or sets the title cell style applier.
43 | ///
44 | /// The title cell style applier.
45 | public Action TitleCellStyleApplier { get; set; } = DefaultTitleCellStyleApplier;
46 |
47 | ///
48 | /// Gets the fluent configuration entry point for the specified .
49 | ///
50 | /// The .
51 | /// The type of the model.
52 | public FluentConfiguration For() where TModel : class
53 | {
54 | var mc = new FluentConfiguration();
55 |
56 | FluentConfigs[typeof(TModel)] = mc;
57 |
58 | return mc;
59 | }
60 |
61 | ///
62 | /// Gets the model fluent configs.
63 | ///
64 | /// The model fluent configs.
65 | internal IDictionary FluentConfigs { get; } = new Dictionary();
66 |
67 | internal static void DefaultTitleCellStyleApplier(ICellStyle cellStyle, IFont font)
68 | {
69 | cellStyle.Alignment = HorizontalAlignment.Center;
70 | cellStyle.VerticalAlignment = VerticalAlignment.Center;
71 | cellStyle.FillPattern = FillPattern.Bricks;
72 | cellStyle.FillBackgroundColor = HSSFColor.Grey40Percent.Index;
73 | cellStyle.FillForegroundColor = HSSFColor.White.Index;
74 |
75 | font.Boldweight = (short)FontBoldWeight.Bold;
76 | cellStyle.SetFont(font);
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Extensions/IEnumerableNpoiExtensions.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | using System;
6 | using System.Globalization;
7 | using System.Linq;
8 | using System.Collections.Generic;
9 | using System.IO;
10 | using System.Reflection;
11 | // NPOI
12 | using HPSF;
13 | using HSSF.UserModel;
14 | using HSSF.Util;
15 | using SS.UserModel;
16 | using SS.Util;
17 | using XSSF.UserModel;
18 |
19 | ///
20 | /// Defines some extensions for that using NPOI to provides excel functionality.
21 | ///
22 | public static class IEnumerableNpoiExtensions
23 | {
24 | public static byte[] ToExcelContent(this IEnumerable source, string sheetName = "sheet0")
25 | {
26 | if (source == null)
27 | {
28 | throw new ArgumentNullException(nameof(source));
29 | }
30 |
31 | var book = source.ToWorkbook(null, sheetName);
32 |
33 | using (var ms = new MemoryStream())
34 | {
35 | book.Write(ms);
36 | return ms.ToArray();
37 | }
38 | }
39 |
40 | public static void ToExcel(this IEnumerable source, string excelFile, string sheetName = "sheet0") where T : class
41 | {
42 | if (source == null)
43 | {
44 | throw new ArgumentNullException(nameof(source));
45 | }
46 |
47 | if (Path.GetExtension(excelFile).Equals(".xls"))
48 | {
49 | Excel.Setting.UserXlsx = false;
50 | }
51 | else if (Path.GetExtension(excelFile).Equals(".xlsx"))
52 | {
53 | Excel.Setting.UserXlsx = true;
54 | }
55 | else
56 | {
57 | throw new NotSupportedException($"not an excel file extension (*.xls | *.xlsx) {excelFile}");
58 | }
59 |
60 | var book = source.ToWorkbook(excelFile, sheetName);
61 |
62 | // Write the stream data of workbook to file
63 | using (var stream = new FileStream(excelFile, FileMode.OpenOrCreate, FileAccess.Write))
64 | {
65 | book.Write(stream);
66 | }
67 | }
68 |
69 | internal static IWorkbook ToWorkbook(this IEnumerable source, string excelFile, string sheetName)
70 | {
71 | // can static properties or only instance properties?
72 | var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty);
73 |
74 | bool fluentConfigEnabled = false;
75 | // get the fluent config
76 | if (Excel.Setting.FluentConfigs.TryGetValue(typeof(T), out var fluentConfig))
77 | {
78 | fluentConfigEnabled = true;
79 | }
80 |
81 | // find out the configs
82 | var cellConfigs = new CellConfig[properties.Length];
83 | for (var j = 0; j < properties.Length; j++)
84 | {
85 | var property = properties[j];
86 |
87 | // get the property config
88 | if (fluentConfigEnabled && fluentConfig.PropertyConfigs.TryGetValue(property, out var pc))
89 | {
90 | // fluent configure first(Hight Priority)
91 | cellConfigs[j] = pc.CellConfig;
92 | }
93 | else
94 | {
95 | var attrs = property.GetCustomAttributes(typeof(ColumnAttribute), true) as ColumnAttribute[];
96 | if (attrs != null && attrs.Length > 0)
97 | {
98 | cellConfigs[j] = attrs[0].CellConfig;
99 | }
100 | else
101 | {
102 | cellConfigs[j] = null;
103 | }
104 | }
105 | }
106 |
107 | // init work book.
108 | var workbook = InitializeWorkbook(excelFile);
109 |
110 | // new sheet
111 | var sheet = workbook.CreateSheet(sheetName);
112 |
113 | // cache cell styles
114 | var cellStyles = new Dictionary();
115 |
116 | // title row cell style
117 | ICellStyle titleStyle = null;
118 | if (Excel.Setting.TitleCellStyleApplier != null)
119 | {
120 | titleStyle = workbook.CreateCellStyle();
121 | var font = workbook.CreateFont();
122 | Excel.Setting.TitleCellStyleApplier(titleStyle, font);
123 | }
124 |
125 | var titleRow = sheet.CreateRow(0);
126 | var rowIndex = 1;
127 | foreach (var item in source)
128 | {
129 | var row = sheet.CreateRow(rowIndex);
130 | for (var i = 0; i < properties.Length; i++)
131 | {
132 | var property = properties[i];
133 |
134 | int index = i;
135 | var config = cellConfigs[i];
136 | if (config != null)
137 | {
138 | if (config.IsIgnored)
139 | continue;
140 |
141 | index = config.Index;
142 | }
143 |
144 | // this is the first time.
145 | if (rowIndex == 1)
146 | {
147 | // if not title, using property name as title.
148 | var title = property.Name;
149 | if (!string.IsNullOrEmpty(config.Title))
150 | {
151 | title = config.Title;
152 | }
153 |
154 | if (!string.IsNullOrEmpty(config.Formatter))
155 | {
156 | try
157 | {
158 | var style = workbook.CreateCellStyle();
159 |
160 | var dataFormat = workbook.CreateDataFormat();
161 |
162 | style.DataFormat = dataFormat.GetFormat(config.Formatter);
163 |
164 | cellStyles[i] = style;
165 | }
166 | catch (Exception ex)
167 | {
168 | // the formatter isn't excel supported formatter
169 | System.Diagnostics.Debug.WriteLine(ex.ToString());
170 | }
171 | }
172 |
173 | var titleCell = titleRow.CreateCell(index);
174 | titleCell.CellStyle = titleStyle;
175 | titleCell.SetCellValue(title);
176 | }
177 |
178 | var value = property.GetValue(item, null);
179 | if (value == null)
180 | continue;
181 |
182 | var cell = row.CreateCell(index);
183 | if (cellStyles.TryGetValue(i, out var cellStyle))
184 | {
185 | cell.CellStyle = cellStyle;
186 |
187 | var unwrapType = property.PropertyType.UnwrapNullableType();
188 | if (unwrapType == typeof(bool))
189 | {
190 | cell.SetCellValue((bool)value);
191 | }
192 | else if (unwrapType == typeof(DateTime))
193 | {
194 | cell.SetCellValue(Convert.ToDateTime(value));
195 | }
196 | else if (unwrapType == typeof(double))
197 | {
198 | cell.SetCellValue(Convert.ToDouble(value));
199 | }
200 | else if (value is IFormattable)
201 | {
202 | var fv = value as IFormattable;
203 | cell.SetCellValue(fv.ToString(config.Formatter, CultureInfo.CurrentCulture));
204 | }
205 | else
206 | {
207 | cell.SetCellValue(value.ToString());
208 | }
209 | }
210 | else if (value is IFormattable)
211 | {
212 | var fv = value as IFormattable;
213 | cell.SetCellValue(fv.ToString(config.Formatter, CultureInfo.CurrentCulture));
214 | }
215 | else
216 | {
217 | cell.SetCellValue(value.ToString());
218 | }
219 | }
220 |
221 | rowIndex++;
222 | }
223 |
224 | // merge cells
225 | var mergableConfigs = cellConfigs.Where(c => c != null && c.AllowMerge).ToList();
226 | if (mergableConfigs.Any())
227 | {
228 | // merge cell style
229 | var vStyle = workbook.CreateCellStyle();
230 | vStyle.VerticalAlignment = VerticalAlignment.Center;
231 |
232 | foreach (var config in mergableConfigs)
233 | {
234 | object previous = null;
235 | int rowspan = 0, row = 1;
236 | for (row = 1; row < rowIndex; row++)
237 | {
238 | var value = sheet.GetRow(row).GetCellValue(config.Index);
239 | if (object.Equals(previous, value) && value != null)
240 | {
241 | rowspan++;
242 | }
243 | else
244 | {
245 | if (rowspan > 1)
246 | {
247 | sheet.GetRow(row - rowspan).Cells[config.Index].CellStyle = vStyle;
248 | sheet.AddMergedRegion(new CellRangeAddress(row - rowspan, row - 1, config.Index, config.Index));
249 | }
250 | rowspan = 1;
251 | previous = value;
252 | }
253 | }
254 |
255 | // in what case? -> all rows need to be merged
256 | if (rowspan > 1)
257 | {
258 | sheet.GetRow(row - rowspan).Cells[config.Index].CellStyle = vStyle;
259 | sheet.AddMergedRegion(new CellRangeAddress(row - rowspan, row - 1, config.Index, config.Index));
260 | }
261 | }
262 | }
263 |
264 | if (rowIndex > 1)
265 | {
266 | var statistics = new List();
267 | var filterConfigs = new List();
268 | var freezeConfigs = new List();
269 | if (fluentConfigEnabled)
270 | {
271 | statistics.AddRange(fluentConfig.StatisticsConfigs);
272 | freezeConfigs.AddRange(fluentConfig.FreezeConfigs);
273 | filterConfigs.AddRange(fluentConfig.FilterConfigs);
274 | }
275 | else
276 | {
277 | var attributes = typeof(T).GetCustomAttributes(typeof(StatisticsAttribute), true) as StatisticsAttribute[];
278 | if (attributes != null && attributes.Length > 0)
279 | {
280 | foreach (var item in attributes)
281 | {
282 | statistics.Add(item.StatisticsConfig);
283 | }
284 | }
285 |
286 | var freezes = typeof(T).GetCustomAttributes(typeof(FreezeAttribute), true) as FreezeAttribute[];
287 | if (freezes != null && freezes.Length > 0)
288 | {
289 | foreach (var item in freezes)
290 | {
291 | freezeConfigs.Add(item.FreezeConfig);
292 | }
293 | }
294 |
295 | var filters = typeof(T).GetCustomAttributes(typeof(FilterAttribute), true) as FilterAttribute[];
296 | if (filters != null && filters.Length > 0)
297 | {
298 | foreach (var item in filters)
299 | {
300 | filterConfigs.Add(item.FilterConfig);
301 | }
302 | }
303 | }
304 |
305 | // statistics row
306 | foreach (var item in statistics)
307 | {
308 | var lastRow = sheet.CreateRow(rowIndex);
309 | var cell = lastRow.CreateCell(0);
310 | cell.SetCellValue(item.Name);
311 | foreach (var column in item.Columns)
312 | {
313 | cell = lastRow.CreateCell(column);
314 | cell.CellFormula = $"{item.Formula}({GetCellPosition(1, column)}:{GetCellPosition(rowIndex - 1, column)})";
315 | }
316 |
317 | rowIndex++;
318 | }
319 |
320 | // set the freeze
321 | foreach (var freeze in freezeConfigs)
322 | {
323 | sheet.CreateFreezePane(freeze.ColSplit, freeze.RowSplit, freeze.LeftMostColumn, freeze.TopRow);
324 | }
325 |
326 | // set the auto filter
327 | foreach (var filter in filterConfigs)
328 | {
329 | sheet.SetAutoFilter(new CellRangeAddress(filter.FirstRow, filter.LastRow ?? rowIndex, filter.FirstCol, filter.LastCol));
330 | }
331 | }
332 |
333 | // autosize the all columns
334 | for (int i = 0; i < properties.Length; i++)
335 | {
336 | sheet.AutoSizeColumn(i);
337 | }
338 |
339 | return workbook;
340 | }
341 |
342 | private static IWorkbook InitializeWorkbook(string excelFile)
343 | {
344 | var setting = Excel.Setting;
345 | if (setting.UserXlsx)
346 | {
347 | if (!string.IsNullOrEmpty(excelFile) && File.Exists(excelFile))
348 | {
349 | using (var file = new FileStream(excelFile, FileMode.Open, FileAccess.Read))
350 | {
351 | return new XSSFWorkbook(file);
352 | }
353 | }
354 | else
355 | {
356 | return new XSSFWorkbook();
357 | }
358 | }
359 | else
360 | {
361 | if (!string.IsNullOrEmpty(excelFile) && File.Exists(excelFile))
362 | {
363 | using (var file = new FileStream(excelFile, FileMode.Open, FileAccess.Read))
364 | {
365 | return new HSSFWorkbook(file);
366 | }
367 | }
368 | else
369 | {
370 | var hssf = new HSSFWorkbook();
371 |
372 | var dsi = PropertySetFactory.CreateDocumentSummaryInformation();
373 | dsi.Company = setting.Company;
374 | hssf.DocumentSummaryInformation = dsi;
375 |
376 | var si = PropertySetFactory.CreateSummaryInformation();
377 | si.Author = setting.Author;
378 | si.Subject = setting.Subject;
379 | hssf.SummaryInformation = si;
380 |
381 | return hssf;
382 | }
383 | }
384 | }
385 |
386 | private static string GetCellPosition(int row, int col)
387 | {
388 | col = Convert.ToInt32('A') + col;
389 | row = row + 1;
390 | return ((char)col) + row.ToString();
391 | }
392 | }
393 | }
--------------------------------------------------------------------------------
/src/Extensions/TypeExtensions.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Reflection;
6 |
7 | namespace NPOI.Extension
8 | {
9 | ///
10 | /// The extension methods for .
11 | ///
12 | internal static class TypeExtensions
13 | {
14 | ///
15 | /// Gets the unwrap nullalble type if the the is nullable type or the type self.
16 | ///
17 | ///
18 | ///
19 | public static Type UnwrapNullableType(this Type type) => Nullable.GetUnderlyingType(type) ?? type;
20 |
21 | ///
22 | /// Determines the specified type is primitive type.
23 | ///
24 | ///
25 | ///
26 | public static bool IsPrimitive(this Type type) => type.IsInteger() || type.IsNonIntegerPrimitive();
27 |
28 | ///
29 | /// Determines the specified type is integer type.
30 | ///
31 | ///
32 | ///
33 | public static bool IsInteger(this Type type)
34 | {
35 | type = type.UnwrapNullableType();
36 |
37 | return (type == typeof(int))
38 | || (type == typeof(long))
39 | || (type == typeof(short))
40 | || (type == typeof(byte))
41 | || (type == typeof(uint))
42 | || (type == typeof(ulong))
43 | || (type == typeof(ushort))
44 | || (type == typeof(sbyte))
45 | || (type == typeof(char));
46 | }
47 |
48 | public static object GetDefaultValue(this Type type)
49 | {
50 | if (!type.GetTypeInfo().IsValueType)
51 | {
52 | return null;
53 | }
54 |
55 | // A bit of perf code to avoid calling Activator.CreateInstance for common types and
56 | // to avoid boxing on every call. This is about 50% faster than just calling CreateInstance
57 | // for all value types.
58 | object value;
59 | return _commonTypeDictionary.TryGetValue(type, out value)
60 | ? value
61 | : Activator.CreateInstance(type);
62 | }
63 |
64 | private static bool IsNonIntegerPrimitive(this Type type)
65 | {
66 | type = type.UnwrapNullableType();
67 |
68 | return (type == typeof(bool))
69 | || (type == typeof(byte[]))
70 | || (type == typeof(DateTime))
71 | || (type == typeof(TimeSpan))
72 | || (type == typeof(DateTimeOffset))
73 | || (type == typeof(decimal))
74 | || (type == typeof(double))
75 | || (type == typeof(float))
76 | || (type == typeof(Guid))
77 | || (type == typeof(string))
78 | || type.GetTypeInfo().IsEnum;
79 | }
80 |
81 | private static readonly Dictionary _commonTypeDictionary = new Dictionary
82 | {
83 | { typeof(Guid), default(Guid) },
84 | { typeof(TimeSpan), default(TimeSpan) },
85 | { typeof(DateTime), default(DateTime) },
86 | { typeof(DateTimeOffset), default(DateTimeOffset) },
87 | { typeof(char), default(char) },
88 | { typeof(int), default(int) },
89 | { typeof(uint), default(uint) },
90 | { typeof(long), default(long) },
91 | { typeof(ulong), default(ulong) },
92 | { typeof(short), default(short) },
93 | { typeof(ushort), default(ushort) },
94 | { typeof(byte), default(byte) },
95 | { typeof(sbyte), default(sbyte) },
96 | { typeof(bool), default(bool) },
97 | { typeof(double), default(double) },
98 | { typeof(float), default(float) },
99 | };
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/FluentConfiguration/FluentConfiguration.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | using System;
6 | using System.Reflection;
7 | using System.Linq.Expressions;
8 | using System.Collections.Generic;
9 |
10 | ///
11 | /// Represents the fluent configuration for the specfidied model.
12 | ///
13 | /// The type of model.
14 | public class FluentConfiguration : IFluentConfiguration where TModel : class
15 | {
16 | private IDictionary _propertyConfigs;
17 | private IList _statisticsConfigs;
18 | private IList _filterConfigs;
19 | private IList _freezeConfigs;
20 |
21 | ///
22 | /// Initializes a new instance of the class.
23 | ///
24 | public FluentConfiguration()
25 | {
26 | _propertyConfigs = new Dictionary();
27 | _statisticsConfigs = new List();
28 | _filterConfigs = new List();
29 | _freezeConfigs = new List();
30 | }
31 |
32 | ///
33 | /// Gets the property configs.
34 | ///
35 | /// The property configs.
36 | IDictionary IFluentConfiguration.PropertyConfigs
37 | {
38 | get
39 | {
40 | return _propertyConfigs;
41 | }
42 | }
43 |
44 | ///
45 | /// Gets the statistics configs.
46 | ///
47 | /// The statistics config.
48 | IList IFluentConfiguration.StatisticsConfigs
49 | {
50 | get
51 | {
52 | return _statisticsConfigs;
53 | }
54 | }
55 |
56 | ///
57 | /// Gets the filter configs.
58 | ///
59 | /// The filter config.
60 | IList IFluentConfiguration.FilterConfigs
61 | {
62 | get
63 | {
64 | return _filterConfigs;
65 | }
66 | }
67 |
68 | ///
69 | /// Gets the freeze configs.
70 | ///
71 | /// The freeze config.
72 | IList IFluentConfiguration.FreezeConfigs
73 | {
74 | get
75 | {
76 | return _freezeConfigs;
77 | }
78 | }
79 |
80 | ///
81 | /// Gets the property configuration by the specified property expression for the specified and its .
82 | ///
83 | /// The .
84 | /// The property expression.
85 | /// The type of parameter.
86 | public PropertyConfiguration Property(Expression> propertyExpression)
87 | {
88 | var pc = new PropertyConfiguration();
89 |
90 | var propertyInfo = GetPropertyInfo(propertyExpression);
91 |
92 | _propertyConfigs[propertyInfo] = pc;
93 |
94 | return pc;
95 | }
96 |
97 | ///
98 | /// Configures the statistics for the specified . Only for vertical, not for horizontal statistics.
99 | ///
100 | /// The .
101 | /// The statistics name. (e.g. Total). In current version, the default name location is (last row, first cell)
102 | /// The cell formula, such as SUM, AVERAGE and so on, which applyable for vertical statistics..
103 | /// The column indexes for statistics. if is SUM, and is [1,3],
104 | /// for example, the column No. 1 and 3 will be SUM for first row to last row.
105 | public FluentConfiguration HasStatistics(string name, string formula, params int[] columnIndexes)
106 | {
107 | var statistics = new StatisticsConfig
108 | {
109 | Name = name,
110 | Formula = formula,
111 | Columns = columnIndexes,
112 | };
113 |
114 | _statisticsConfigs.Add(statistics);
115 |
116 | return this;
117 | }
118 |
119 | ///
120 | /// Configures the excel filter behaviors for the specified .
121 | ///
122 | /// The .
123 | /// The first column index.
124 | /// The last column index.
125 | /// The first row index.
126 | /// The last row index. If is null, the value is dynamic calculate by code.
127 | public FluentConfiguration HasFilter(int firstColumn, int lastColumn, int firstRow, int? lastRow = null)
128 | {
129 | var filter = new FilterConfig
130 | {
131 | FirstCol = firstColumn,
132 | FirstRow = firstRow,
133 | LastCol = lastColumn,
134 | LastRow = lastRow,
135 | };
136 |
137 | _filterConfigs.Add(filter);
138 |
139 | return this;
140 | }
141 |
142 | ///
143 | /// Configures the excel freeze behaviors for the specified .
144 | ///
145 | /// The .
146 | /// The column number to split.
147 | /// The row number to split.param>
148 | /// The left most culomn index.
149 | /// The top most row index.
150 | public FluentConfiguration HasFreeze(int columnSplit, int rowSplit, int leftMostColumn, int topMostRow)
151 | {
152 | var freeze = new FreezeConfig
153 | {
154 | ColSplit = columnSplit,
155 | RowSplit = rowSplit,
156 | LeftMostColumn = leftMostColumn,
157 | TopRow = topMostRow,
158 | };
159 |
160 | _freezeConfigs.Add(freeze);
161 |
162 | return this;
163 | }
164 |
165 | private PropertyInfo GetPropertyInfo(Expression> propertyExpression)
166 | {
167 | if (propertyExpression.NodeType != ExpressionType.Lambda)
168 | {
169 | throw new ArgumentException($"{nameof(propertyExpression)} must be lambda expression", nameof(propertyExpression));
170 | }
171 |
172 | var lambda = (LambdaExpression)propertyExpression;
173 |
174 | var memberExpression = ExtractMemberExpression(lambda.Body);
175 | if (memberExpression == null)
176 | {
177 | throw new ArgumentException($"{nameof(propertyExpression)} must be lambda expression", nameof(propertyExpression));
178 | }
179 |
180 | if (memberExpression.Member.DeclaringType == null)
181 | {
182 | throw new InvalidOperationException("Property does not have declaring type");
183 | }
184 |
185 | return memberExpression.Member.DeclaringType.GetProperty(memberExpression.Member.Name);
186 | }
187 |
188 | private MemberExpression ExtractMemberExpression(Expression expression)
189 | {
190 | if (expression.NodeType == ExpressionType.MemberAccess)
191 | {
192 | return ((MemberExpression)expression);
193 | }
194 |
195 | if (expression.NodeType == ExpressionType.Convert)
196 | {
197 | var operand = ((UnaryExpression)expression).Operand;
198 | return ExtractMemberExpression(operand);
199 | }
200 |
201 | return null;
202 | }
203 | }
204 | }
--------------------------------------------------------------------------------
/src/FluentConfiguration/IFluentConfiguration.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | using System.Reflection;
6 | using System.Collections.Generic;
7 |
8 | ///
9 | /// Provides the interfaces for the fluent configuration.
10 | ///
11 | internal interface IFluentConfiguration
12 | {
13 | ///
14 | /// Gets the property configs.
15 | ///
16 | /// The property configs.
17 | IDictionary PropertyConfigs { get; }
18 |
19 | ///
20 | /// Gets the statistics configs.
21 | ///
22 | /// The statistics config.
23 | IList StatisticsConfigs { get; }
24 |
25 | ///
26 | /// Gets the filter configs.
27 | ///
28 | /// The filter config.
29 | IList FilterConfigs { get; }
30 |
31 | ///
32 | /// Gets the freeze configs.
33 | ///
34 | /// The freeze config.
35 | IList FreezeConfigs { get; }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/FluentConfiguration/PropertyConfiguration.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved
2 |
3 | namespace NPOI.Extension
4 | {
5 | ///
6 | /// Represents the configuration for the specfidied property.
7 | ///
8 | public class PropertyConfiguration
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | public PropertyConfiguration()
14 | {
15 | CellConfig = new CellConfig();
16 | }
17 |
18 | ///
19 | /// Gets the cell config.
20 | ///
21 | /// The cell config.
22 | internal CellConfig CellConfig { get; }
23 |
24 | ///
25 | /// Configures the excel cell index for the property.
26 | ///
27 | /// The .
28 | /// The excel cell index.
29 | ///
30 | /// If index was not set and AutoIndex is true NPOI.Extension will try to autodiscover the column index by its title setting.
31 | ///
32 | public PropertyConfiguration HasExcelIndex(int index)
33 | {
34 | CellConfig.Index = index;
35 |
36 | return this;
37 | }
38 |
39 | ///
40 | /// Configures the excel title (first row) for the property.
41 | ///
42 | /// The .
43 | /// The excel cell title (fist row).
44 | ///
45 | /// If the title is string.Empty, will not set the excel cell, and if the title is NULL, the property's name will be used.
46 | ///
47 | public PropertyConfiguration HasExcelTitle(string title)
48 | {
49 | CellConfig.Title = title;
50 |
51 | return this;
52 | }
53 |
54 | ///
55 | /// Configures the formatter will be used for formatting the value for the property.
56 | ///
57 | /// The .
58 | /// The formatter will be used for formatting the value.
59 | ///
60 | /// If the title is string.Empty, will not set the excel cell, and if the title is NULL, the property's name will be used.
61 | ///
62 | public PropertyConfiguration HasDataFormatter(string formatter)
63 | {
64 | CellConfig.Formatter = formatter;
65 |
66 | return this;
67 | }
68 |
69 | ///
70 | /// Configures whether to autodiscover the column index by its title setting for the specified property.
71 | ///
72 | /// The .
73 | ///
74 | /// If index was not set and AutoIndex is true NPOI.Extension will try to autodiscover the column index by its title setting.
75 | ///
76 | public PropertyConfiguration HasAutoIndex()
77 | {
78 | CellConfig.AutoIndex = true;
79 |
80 | return this;
81 | }
82 |
83 | ///
84 | /// Configures whether to allow merge the same value cells for the specified property.
85 | ///
86 | /// The .
87 | public PropertyConfiguration IsMergeEnabled()
88 | {
89 | CellConfig.AllowMerge = true;
90 |
91 | return this;
92 | }
93 |
94 | ///
95 | /// Configures whether to ignore the specified property.
96 | ///
97 | public void IsIgnored()
98 | {
99 | CellConfig.IsIgnored = true;
100 | }
101 |
102 | ///
103 | /// Configures the excel cell for the property.
104 | ///
105 | /// The excel cell index.
106 | /// The excel cell title (fist row).
107 | /// The formatter will be used for formatting the value.
108 | /// If set to true allow merge the same value cells.
109 | public void HasExcelCell(int index, string title, string formatter, bool allowMerge)
110 | {
111 | CellConfig.Index = index;
112 | CellConfig.Title = title;
113 | CellConfig.Formatter = formatter;
114 | CellConfig.AutoIndex = false;
115 | CellConfig.AllowMerge = allowMerge;
116 | }
117 |
118 | ///
119 | /// Configures the excel cell for the property. This method will try to autodiscover the column index by its
120 | ///
121 | /// The excel cell title (fist row).
122 | /// The formatter will be used for formatting the value.
123 | /// If set to true allow merge the same value cells.
124 | ///
125 | /// This method will try to autodiscover the column index by its
126 | ///
127 | public void HasExcelCell(string title, string formatter, bool allowMerge)
128 | {
129 | CellConfig.Index = -1;
130 | CellConfig.Title = title;
131 | CellConfig.Formatter = formatter;
132 | CellConfig.AutoIndex = true;
133 | CellConfig.AllowMerge = allowMerge;
134 | }
135 | }
136 | }
--------------------------------------------------------------------------------
/src/Internal/CellConfig.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | ///
6 | /// Represents the excel cell configuration for the specified model's property.
7 | ///
8 | internal class CellConfig
9 | {
10 | ///
11 | /// Gets or sets the title of the column.
12 | ///
13 | ///
14 | /// If the is null or empty, will use property name as the excel column title.
15 | ///
16 | public string Title { get; set; }
17 |
18 | ///
19 | /// If was not set and AutoIndex is true NPOI.Extension will try to autodiscover the column index by its property.
20 | ///
21 | public bool AutoIndex { get; set; }
22 |
23 | ///
24 | /// Gets or sets the column index.
25 | ///
26 | /// The index.
27 | public int Index { get; set; } = -1;
28 |
29 | ///
30 | /// Gets or sets a value indicating whether allow merge the same value cells.
31 | ///
32 | public bool AllowMerge { get; set; }
33 |
34 | ///
35 | /// Gets or sets a value indicating whether this value of the property is ignored.
36 | ///
37 | /// true if is ignored; otherwise, false.
38 | public bool IsIgnored { get; set; }
39 |
40 | ///
41 | /// Gets or sets the formatter for formatting the value.
42 | ///
43 | /// The formatter.
44 | public string Formatter { get; set; }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Internal/FilterConfig.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | ///
6 | /// Represents the excel fileter configration for the specified model.
7 | ///
8 | internal class FilterConfig
9 | {
10 | ///
11 | /// Gets or sets the first row index.
12 | ///
13 | public int FirstRow { get; set; }
14 |
15 | ///
16 | /// Gets or sets the last row index.
17 | ///
18 | ///
19 | /// If the is null, the value is dynamic calculate by code.
20 | ///
21 | public int? LastRow { get; set; } = null;
22 |
23 | ///
24 | /// Gets or sets the first column index.
25 | ///
26 | public int FirstCol { get; set; }
27 |
28 | ///
29 | /// Gets or sets the last column index.
30 | ///
31 | public int LastCol { get; set; }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Internal/FreezeConfig.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | ///
6 | /// Represents the excel freeze configuration for the specified model.
7 | ///
8 | internal class FreezeConfig
9 | {
10 | ///
11 | /// Gets or sets the column number to split.
12 | ///
13 | public int ColSplit { get; set; } = 0;
14 |
15 | ///
16 | /// Gets or sets the row number to split.
17 | ///
18 | public int RowSplit { get; set; } = 1;
19 |
20 | ///
21 | /// Gets or sets the left most culomn index.
22 | ///
23 | public int LeftMostColumn { get; set; } = 0;
24 |
25 | ///
26 | /// Gets or sets the top most row index.
27 | ///
28 | public int TopRow { get; set; } = 1;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Internal/StatisticsConfig.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) rigofunc (xuyingting). All rights reserved.
2 |
3 | namespace NPOI.Extension
4 | {
5 | ///
6 | /// Represents the excel statistics for the specified model.
7 | ///
8 | internal class StatisticsConfig
9 | {
10 | ///
11 | /// Gets or sets the statistics name. (e.g. Total)
12 | ///
13 | ///
14 | /// In current version, the default name location is (last row, first cell)
15 | ///
16 | public string Name { get; set; }
17 |
18 | ///
19 | /// Gets or sets the cell formula, such as SUM, AVERAGE and so on, which applyable for vertical statistics.
20 | ///
21 | public string Formula { get; set; }
22 |
23 | ///
24 | /// Gets or sets the column indexes for statistics. if is SUM,
25 | /// and is [1,3], for example, the column No. 1 and 3 will be
26 | /// SUM for first row to last row.
27 | ///
28 | public int[] Columns { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/NPOI.Extension.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {5ADDD29D-B3AF-4966-B730-5E5192D0E9DF}
8 | Library
9 | Properties
10 | NPOI.Extension
11 | NPOI.Extension
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 | AnyCPU
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 | AnyCPU
34 |
35 |
36 |
37 | ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll
38 | True
39 |
40 |
41 | ..\packages\NPOI.2.3.0\lib\net40\NPOI.dll
42 |
43 |
44 | ..\packages\NPOI.2.3.0\lib\net40\NPOI.OOXML.dll
45 |
46 |
47 | ..\packages\NPOI.2.3.0\lib\net40\NPOI.OpenXml4Net.dll
48 |
49 |
50 | ..\packages\NPOI.2.3.0\lib\net40\NPOI.OpenXmlFormats.dll
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 | Designer
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
92 |
--------------------------------------------------------------------------------
/src/NPOI.Extension.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $id$
5 | $version$
6 | $title$
7 | $author$
8 | $author$
9 | https://github.com/xyting/NPOI.Extension/blob/master/LICENSE
10 | https://github.com/xyting/NPOI.Extension
11 | https://nuget.org/Content/Images/packageDefaultIcon.png
12 | false
13 | $description$
14 |
15 | Copyright © rigofunc (xuyingting). All rights reserved.
16 | npoi, xlsx, xls, Excel, Word, docx
17 |
18 |
--------------------------------------------------------------------------------
/src/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("NPOI.Extension")]
8 | [assembly: AssemblyDescription("The extensions of NPOI, which provides IEnumerable<T> save to and load from excel.")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("rigofunc (xuyingting)")]
11 | [assembly: AssemblyProduct("NPOI.Extension")]
12 | [assembly: AssemblyCopyright("Copyright © rigofunc (xuyingting). All rights reserved.")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("5addd29d-b3af-4966-b730-5e5192d0e9df")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("2.2.0.0")]
35 | [assembly: AssemblyFileVersion("2.2.0.0")]
36 |
--------------------------------------------------------------------------------
/src/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------