├── .gitignore ├── CHANGELOG.md ├── ConsoleTableExtSolution.sln ├── FUNDING.yml ├── LICENSE ├── README.md ├── Src ├── ConsoleTableApp │ ├── App.config │ ├── ConsoleTableApp.csproj │ ├── Program.cs │ └── Properties │ │ └── AssemblyInfo.cs ├── ConsoleTableExt.Tests │ ├── ConsoleTableExt.Tests.csproj │ ├── CustomFormats.cs │ ├── DefaultFormats.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── SampleData.cs │ └── packages.config └── ConsoleTableExt │ ├── CharMapDefinition.cs │ ├── ConsoleColorNullable.cs │ ├── ConsoleTableBuilder.cs │ ├── ConsoleTableBuilderExtensions.cs │ ├── ConsoleTableExt.csproj │ ├── Enums │ ├── CharMapPositions.cs │ ├── ConsoleTableBuilderFormat.cs │ ├── HeaderCharMapPositions.cs │ ├── MetaRowPositions.cs │ ├── TableAligntment.cs │ └── TextAligntment.cs │ └── StringExtensions.cs ├── test-regex.txt └── wiki └── Images ├── CharMapPositions.png ├── HeaderCharMapPositions.png ├── demo.png ├── demo1.png └── icon.ico /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /ConsoleTableExtSolution/.vs/ConsoleTableExtSolution/v15 6 | /Src/ConsoleTableExt/bin/Debug 7 | /Src/ConsoleTableExt/obj/Debug 8 | /Src/ConsoleTableExtTest/bin/Debug 9 | /Src/ConsoleTableExtTest/obj/Debug 10 | /ConsoleTableExtSolution/packages/NUnit.3.9.0 11 | /packages/NUnit.3.9.0 12 | /.vs/ConsoleTableExtSolution/v15 13 | /Src/ConsoleTableApp/bin/Debug 14 | /Src/ConsoleTableApp/obj/Debug 15 | /Src/ConsoleTableExt/obj 16 | /Src/ConsoleTableApp/bin/Release 17 | /Src/ConsoleTableApp/obj/Release 18 | /Src/ConsoleTableExt/bin/Release 19 | .vs/ 20 | /ConsoleTableExt.Tests/bin/Debug 21 | /ConsoleTableExt.Tests/obj/Debug 22 | /packages 23 | /ConsoleTableExt.Tests/bin/Release 24 | /ConsoleTableExt.Tests/obj/Release 25 | /Src/ConsoleTableExt.Tests/bin/Debug 26 | /Src/ConsoleTableExt.Tests/bin/Release 27 | /Src/ConsoleTableExt.Tests/obj/Debug 28 | /Src/ConsoleTableExt.Tests/obj/Release 29 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 3.2.0 (2022-07-10) 2 | - fix formatter bug (thanks @maikebing) 3 | 4 | ## 3.1.9 (2021-08-16) 5 | - fix bug: #32 Misplaced format If there are UTF-8 characters in the data (thanks @haptear) 6 | 7 | ## 3.1.8 (2021-08-07) 8 | - fix bug: #30 Misplaced format If there are UTF-8 characters in the data (thanks @haptear) 9 | 10 | ## 3.1.7 (2021-04-20) 11 | - allow developer support their datasource 12 | 13 | ## 3.1.6 (2021-04-20) 14 | - fix bug #25 List crashed when using with string type 15 | 16 | ## 3.1.5 (2021-02-28) 17 | - introduce WithHeaderTextAlignment, allow user custom header alignment, if it's not defined, it will get text alignment setting, default is left 18 | - fix bug: double header formatter 19 | 20 | ## 3.1.4 (2021-02-22) 21 | - support text alignment center 22 | - change order of items in enum TableAligntment 23 | - remove titleAlignment (use TextAlignment instead) 24 | - restructure project 25 | 26 | ## 3.1.3 (2021-02-21) 27 | - meta rows should be invoked when table is building 28 | - table title alignment (left/right/center) 29 | 30 | ## 3.1.2 (2021-02-12) 31 | - fix title coloring issue when using table alignment center/right 32 | 33 | ## 3.1.1 (2021-02-12) 34 | - fix bug table alignment 35 | 36 | ## 3.1.0 (2021-02-12) 37 | - add test project 38 | - allow custom BorderLeft, BorderRight, BorderTop, BorderBottom 39 | - re-implement custom format builder 40 | - color table title (text color & background color) 41 | - replace old formats (markdown, alternative ...) by CustomFormat builder 42 | - Trim table line/border/divider if all chars are space 43 | - allow custom padding left, padding right of cell content 44 | - rename some variables 45 | - fix bug: allow using title on old formats 46 | - add new method .WithFormatter, allow user re-format content/header value 47 | 48 | ## 3.0.0 (2021-01-31) 49 | - support Table alignment (left, center and right) 50 | - implement table ChartMap to support box-drawing characters and header ChartMap 51 | - does not throw exception when data null/empty 52 | - support DescriptionAttribute for List (custom column header text) 53 | - re-implement MetaRow, support multi row 54 | - remove AppConstants, replace with builder.ColumnLength & builder.RowLength 55 | - remove ConsoleTableBuilderOption 56 | - support TextAligment (Left and Right) 57 | - support Column MinLength 58 | - support table title 59 | - add TrimColumn extension (replace for Option.TrimColumn) 60 | - remove MetaRowPositions.None 61 | - fix typo 62 | 63 | 64 | ## 2.0.3 (2020-08-14) 65 | Feature 66 | - Support .net framework 3.5 67 | -------------------------------------------------------------------------------- /ConsoleTableExtSolution.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTableExt", "Src\ConsoleTableExt\ConsoleTableExt.csproj", "{AB7BC904-D18D-4033-9625-E80877D2F94E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTableApp", "Src\ConsoleTableApp\ConsoleTableApp.csproj", "{C6D28DB6-CDA1-470D-97C2-1A75279BD185}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wiki", "wiki", "{5F169F33-1AF4-4D2B-BE2D-8DA7A57BB9DA}" 11 | ProjectSection(SolutionItems) = preProject 12 | CHANGELOG.md = CHANGELOG.md 13 | README.md = README.md 14 | EndProjectSection 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTableExt.Tests", "Src\ConsoleTableExt.Tests\ConsoleTableExt.Tests.csproj", "{8E7D35C2-3170-42CA-86D2-A5CEFBCE3094}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {AB7BC904-D18D-4033-9625-E80877D2F94E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {AB7BC904-D18D-4033-9625-E80877D2F94E}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {AB7BC904-D18D-4033-9625-E80877D2F94E}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {AB7BC904-D18D-4033-9625-E80877D2F94E}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {C6D28DB6-CDA1-470D-97C2-1A75279BD185}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {C6D28DB6-CDA1-470D-97C2-1A75279BD185}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {C6D28DB6-CDA1-470D-97C2-1A75279BD185}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {C6D28DB6-CDA1-470D-97C2-1A75279BD185}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {8E7D35C2-3170-42CA-86D2-A5CEFBCE3094}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {8E7D35C2-3170-42CA-86D2-A5CEFBCE3094}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {8E7D35C2-3170-42CA-86D2-A5CEFBCE3094}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {8E7D35C2-3170-42CA-86D2-A5CEFBCE3094}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {260FF68D-E553-459A-930C-EB4F3B99752C} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [minhhungit] 2 | ko_fi: minhhungit 3 | custom: ["https://www.paypal.me/minhhungit"] 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jin 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ConsoleTableExt [![Build status](https://ci.appveyor.com/api/projects/status/e4ugtobtcrjhk9p4?svg=true)](https://ci.appveyor.com/project/minhhungit/consoletableext) 2 | 3 | A library to print out a nicely formatted table in a console application C# 4 | 5 | ### Nuget 6 | > https://www.nuget.org/packages/ConsoleTableExt/ 7 | 8 | ### Feature 9 | - Support [box-drawing characters](https://en.wikipedia.org/wiki/Box-drawing_character) 10 | - Table alignment (left right and center) 11 | - Column alignment (left/right/center) 12 | - Table can have TITLE, can change text color and background color of title, support title alignment (left/right/center) 13 | - Support power char-map, strong customization ability 14 | - Contain some popular formas like Markdown table... 15 | - Support text formatter (include header) 16 | - Support many kind data type: DataTable, List... 17 | - Support metadata row (placed at top or bottom of table) 18 | - Column min-length 19 | - support .NET Framework >= 3.5, .NET core 20 | - ... 21 | 22 | ### How to use: 23 | 24 | ```csharp 25 | var tableData = new List> 26 | { 27 | new List{ "Sakura Yamamoto", "Support Engineer", "London", 46}, 28 | new List{ "Serge Baldwin", "Data Coordinator", "San Francisco", 28, "something else" }, 29 | new List{ "Shad Decker", "Regional Director", "Edinburgh"}, 30 | }; 31 | ``` 32 | 33 | **Simple example with default format:** 34 | 35 | ```csharp 36 | ConsoleTableBuilder 37 | .From(tableData) 38 | .ExportAndWriteLine(); 39 | ``` 40 | 41 | **More example with existing format Alternative:** 42 | 43 | ```csharp 44 | ConsoleTableBuilder 45 | .From(tableData) 46 | .WithFormat(ConsoleTableBuilderFormat.Alternative) 47 | .ExportAndWriteLine(TableAligntment.Center); 48 | ``` 49 | 50 | **Advance example with custom format using CharMap:** 51 | 52 | ```csharp 53 | ConsoleTableBuilder 54 | .From(tableData) 55 | .WithTitle("CONTACTS ", ConsoleColor.Yellow, ConsoleColor.DarkGray) 56 | .WithColumn("Id", "First Name", "Sur Name") 57 | .WithMinLength(new Dictionary { 58 | { 1, 25 }, 59 | { 2, 25 } 60 | }) 61 | .WithTextAlignment(new Dictionary 62 | { 63 | {2, TextAligntment.Right } 64 | }) 65 | .WithCharMapDefinition(new Dictionary { 66 | {CharMapPositions.BottomLeft, '=' }, 67 | {CharMapPositions.BottomCenter, '=' }, 68 | {CharMapPositions.BottomRight, '=' }, 69 | {CharMapPositions.BorderTop, '=' }, 70 | {CharMapPositions.BorderBottom, '=' }, 71 | {CharMapPositions.BorderLeft, '|' }, 72 | {CharMapPositions.BorderRight, '|' }, 73 | {CharMapPositions.DividerY, '|' }, 74 | }) 75 | .WithHeaderCharMapDefinition(new Dictionary { 76 | {HeaderCharMapPositions.TopLeft, '=' }, 77 | {HeaderCharMapPositions.TopCenter, '=' }, 78 | {HeaderCharMapPositions.TopRight, '=' }, 79 | {HeaderCharMapPositions.BottomLeft, '|' }, 80 | {HeaderCharMapPositions.BottomCenter, '-' }, 81 | {HeaderCharMapPositions.BottomRight, '|' }, 82 | {HeaderCharMapPositions.Divider, '|' }, 83 | {HeaderCharMapPositions.BorderTop, '=' }, 84 | {HeaderCharMapPositions.BorderBottom, '-' }, 85 | {HeaderCharMapPositions.BorderLeft, '|' }, 86 | {HeaderCharMapPositions.BorderRight, '|' }, 87 | }) 88 | .ExportAndWriteLine(TableAligntment.Right); 89 | ``` 90 | 91 | 92 | 93 | Check more demo here https://github.com/minhhungit/ConsoleTableExt/blob/master/Src/ConsoleTableApp/Program.cs 94 | 95 | 96 | 97 | ### Char Map Definition 98 | 99 | 100 | 101 | ### Header Char Map 102 | 103 | 104 | 105 | 106 | **There are many ways to contribute to ConsoleTableExt, either contribute issue/code directly or buy me a cup of coffee** 107 | 108 | Buy Me a Coffee at ko-fi.com 109 | 110 | ### Inspired by 111 | - khalidabuhakmeh/ConsoleTables 112 | -------------------------------------------------------------------------------- /Src/ConsoleTableApp/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Src/ConsoleTableApp/ConsoleTableApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C6D28DB6-CDA1-470D-97C2-1A75279BD185} 8 | Exe 9 | ConsoleTableApp 10 | ConsoleTableApp 11 | v3.5 12 | 512 13 | 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | {ab7bc904-d18d-4033-9625-e80877d2f94e} 52 | ConsoleTableExt 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Src/ConsoleTableApp/Program.cs: -------------------------------------------------------------------------------- 1 | using ConsoleTableExt; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Data; 6 | using System.Linq; 7 | 8 | namespace ConsoleTableApp 9 | { 10 | class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | var tableDataListListString = new List> 15 | { 16 | new List{ "Sakura Yamamoto", "Support Engineer", "London", 46.ToString(), ""}, 17 | new List{ "Serge Baldwin", "Data Coordinator", "San Francisco", 28.ToString(), "something else" }, 18 | new List{ "Shad Decker", "Regional Director", "Edinburgh", "", ""}, 19 | }; 20 | 21 | ConsoleTableBuilder 22 | .From(tableDataListListString) 23 | .ExportAndWriteLine(); 24 | 25 | var tableDataListListInt = new List> 26 | { 27 | new List{ 1, 2, 3}, 28 | new List{ 4, 5 }, 29 | new List{ 6, 7, 8, 9}, 30 | }; 31 | ConsoleTableBuilder 32 | .From(tableDataListListInt) 33 | .ExportAndWriteLine(); 34 | 35 | 36 | 37 | ConsoleTableBuilder.From(() => { 38 | return new ConsoleTableBaseData 39 | { 40 | Rows = new List> { 41 | new List { "a1", "b1", "c1" } , 42 | new List { "a2", "b2", "c2" } 43 | }, 44 | Column = new List 45 | { 46 | "-A-", "-B-", "-C-" 47 | } 48 | }; 49 | }) 50 | .WithFormat(ConsoleTableBuilderFormat.Alternative) 51 | .ExportAndWriteLine(); 52 | 53 | ConsoleTableBuilder.From(new List { 1, 2, 3, 4, 5, 6 }) 54 | .WithFormat(ConsoleTableBuilderFormat.Alternative) 55 | .ExportAndWriteLine(); 56 | 57 | ConsoleTableBuilder.From(new List { "1", "2", "3", "4", "5", "6" }) 58 | .WithFormat(ConsoleTableBuilderFormat.Alternative) 59 | .WithColumn("I'm a custom name") 60 | .ExportAndWriteLine(); 61 | 62 | ConsoleTableBuilder 63 | .From(SampleTableData()) 64 | .WithFormat(ConsoleTableBuilderFormat.Alternative) 65 | .WithColumnFormatter(1, (text) => $"[ {text.ToUpper()} ]") 66 | .WithFormatter(1, (text) => $"<{text}>") 67 | .WithMinLength(new Dictionary { 68 | { 1, 30 } 69 | }) 70 | .WithTextAlignment(new Dictionary{ 71 | { 1, TextAligntment.Right } 72 | }) 73 | .WithHeaderTextAlignment(new Dictionary { 74 | {1, TextAligntment.Center } 75 | }) 76 | .WithTitle("MY TABLE", ConsoleColor.DarkRed, ConsoleColor.Gray, TextAligntment.Right) 77 | .ExportAndWriteLine(TableAligntment.Center); 78 | 79 | ConsoleTableBuilder.From(new List 80 | { 81 | new object[] { "s" } 82 | }) 83 | .WithTitle("abcdefghlm") 84 | .ExportAndWriteLine(); 85 | 86 | _____________________________PrintDemoDivider(); 87 | 88 | Console.WriteLine("From [DataTable] type and Minimal format:"); 89 | ConsoleTableBuilder.From(SampleTableData()).WithFormat(ConsoleTableBuilderFormat.Minimal).ExportAndWriteLine(); 90 | 91 | _____________________________PrintDemoDivider(); 92 | 93 | var strBuilder01 = 94 | ConsoleTableBuilder 95 | .From(SampleTableData()) 96 | .WithPaddingLeft(string.Empty) 97 | .WithCharMapDefinition() 98 | .Export(); 99 | Console.WriteLine(strBuilder01); 100 | 101 | _____________________________PrintDemoDivider(); 102 | 103 | var strBuilder02 = 104 | ConsoleTableBuilder 105 | .From(SampleTableData()) 106 | .WithTitle("MARKDOWN WITH TITLE ???") 107 | .WithPaddingLeft(string.Empty) 108 | .WithFormat(ConsoleTableBuilderFormat.MarkDown) 109 | .Export(); 110 | Console.WriteLine(strBuilder02); 111 | 112 | _____________________________PrintDemoDivider(); 113 | 114 | var strBuilder03 = 115 | ConsoleTableBuilder 116 | .From(SampleTableData()) 117 | .WithFormatter(4,f=>$"{f:yyyy-MM-dd HH:mm:ss.fff}") 118 | .WithTitle("MARKDOWN WITH TITLE ???") 119 | .WithPaddingLeft(string.Empty) 120 | .WithFormat(ConsoleTableBuilderFormat.MarkDown) 121 | .Export(); 122 | Console.WriteLine(strBuilder03); 123 | 124 | _____________________________PrintDemoDivider(); 125 | 126 | Console.WriteLine("Text alignment with table title"); 127 | ConsoleTableBuilder.From(SampleListData) 128 | //.WithFormat(ConsoleTableBuilderFormat.MarkDown) 129 | .WithTextAlignment(new Dictionary { 130 | { 0, TextAligntment.Center }, 131 | { 1, TextAligntment.Right }, 132 | { 3, TextAligntment.Right }, 133 | { 100, TextAligntment.Right } 134 | }) 135 | .WithMinLength(new Dictionary { 136 | { 1, 30 } 137 | }) 138 | .WithCharMapDefinition(CharMapDefinition.FramePipDefinition) 139 | .WithTitle("HELLO I AM TITLE", ConsoleColor.Green, ConsoleColor.DarkGray, TextAligntment.Right) 140 | .WithFormatter(1, (text) => 141 | { 142 | return text.ToString().ToUpper().Replace(" ", "-") + " «"; 143 | }) 144 | .ExportAndWriteLine(TableAligntment.Center); 145 | 146 | _____________________________PrintDemoDivider(); 147 | 148 | Console.WriteLine("Text alignment and column min length"); 149 | ConsoleTableBuilder.From(SampleTableData()) 150 | .WithTextAlignment(new Dictionary { 151 | { 0, TextAligntment.Center }, 152 | { 1, TextAligntment.Right }, 153 | { 3, TextAligntment.Center }, 154 | { 100, TextAligntment.Right } 155 | }) 156 | .WithMinLength(new Dictionary { 157 | { 1, 35 }, 158 | { 3, 10 } 159 | }) 160 | .WithFormatter(2, (text) => { 161 | char[] chars = text.ToString().ToCharArray(); 162 | Array.Reverse(chars); 163 | return new String(chars); 164 | }) 165 | .WithTitle("Hello, everyone! This is the LONGEST TEXT EVER! I was inspired by the various other 'longest texts ever' on the internet, and I wanted to make my own. So here it is!".ToUpper(), ConsoleColor.Yellow, ConsoleColor.DarkMagenta) 166 | .WithCharMapDefinition(CharMapDefinition.FrameDoublePipDefinition) 167 | .WithFormatter(3, (text) => { 168 | if (string.IsNullOrEmpty(text.ToString()) || text.ToString().Trim().Length == 0) 169 | { 170 | return "0 $"; 171 | } 172 | else 173 | { 174 | return text + " $"; 175 | } 176 | }) 177 | .WithColumnFormatter(3, (text)=> "#") 178 | .ExportAndWriteLine(); 179 | 180 | _____________________________PrintDemoDivider(); 181 | 182 | Console.WriteLine("Custom format using CharMap"); 183 | ConsoleTableBuilder.From(SampleTableData()) 184 | .WithCharMapDefinition( 185 | CharMapDefinition.FramePipDefinition, 186 | new Dictionary { 187 | {HeaderCharMapPositions.TopLeft, '╒' }, 188 | {HeaderCharMapPositions.TopCenter, '╤' }, 189 | {HeaderCharMapPositions.TopRight, '╕' }, 190 | {HeaderCharMapPositions.BottomLeft, '╞' }, 191 | {HeaderCharMapPositions.BottomCenter, '╪' }, 192 | {HeaderCharMapPositions.BottomRight, '╡' }, 193 | {HeaderCharMapPositions.BorderTop, '═' }, 194 | {HeaderCharMapPositions.BorderRight, '│' }, 195 | {HeaderCharMapPositions.BorderBottom, '═' }, 196 | {HeaderCharMapPositions.BorderLeft, '│' }, 197 | {HeaderCharMapPositions.Divider, '│' }, 198 | }) 199 | .ExportAndWriteLine(TableAligntment.Right); 200 | 201 | _____________________________PrintDemoDivider(); 202 | 203 | Console.WriteLine("Custom format using CharMap: Header has no divider"); 204 | ConsoleTableBuilder.From(SampleTableData()) 205 | .WithCharMapDefinition(CharMapDefinition.FramePipDefinition) 206 | .WithCharMapDefinition( 207 | CharMapDefinition.FramePipDefinition, 208 | new Dictionary { 209 | {HeaderCharMapPositions.TopLeft, '╒' }, 210 | {HeaderCharMapPositions.TopCenter, '═' }, 211 | {HeaderCharMapPositions.TopRight, '╕' }, 212 | {HeaderCharMapPositions.BottomLeft, '╞' }, 213 | {HeaderCharMapPositions.BottomCenter, '╤' }, 214 | {HeaderCharMapPositions.BottomRight, '╡' }, 215 | {HeaderCharMapPositions.BorderTop, '═' }, 216 | {HeaderCharMapPositions.BorderRight, '│' }, 217 | {HeaderCharMapPositions.BorderBottom, '═' }, 218 | {HeaderCharMapPositions.BorderLeft, '│' }, 219 | {HeaderCharMapPositions.Divider, ' ' }, 220 | }) 221 | .ExportAndWriteLine(); 222 | 223 | _____________________________PrintDemoDivider(); 224 | 225 | Console.WriteLine("No header FramePipDefinition"); 226 | ConsoleTableBuilder.From(SampleListData) 227 | .WithCharMapDefinition(CharMapDefinition.FramePipDefinition) 228 | .ExportAndWriteLine(); 229 | 230 | _____________________________PrintDemoDivider(); 231 | 232 | Console.WriteLine("No header FrameDoublePipDefinition:"); 233 | ConsoleTableBuilder.From(SampleListData) 234 | .WithCharMapDefinition(CharMapDefinition.FrameDoublePipDefinition) 235 | .ExportAndWriteLine(); 236 | 237 | 238 | _____________________________PrintDemoDivider(); 239 | 240 | Console.WriteLine("From [DataTable] type and Default format:"); 241 | ConsoleTableBuilder.From(SampleTableData()).ExportAndWriteLine(); 242 | 243 | Console.WriteLine("From [DataTable] type and Minimal format:"); 244 | ConsoleTableBuilder.From(SampleTableData()).WithFormat(ConsoleTableBuilderFormat.Minimal).ExportAndWriteLine(); 245 | 246 | _____________________________PrintDemoDivider(); 247 | 248 | Console.WriteLine("From [List] type and Alternative format:"); 249 | ConsoleTableBuilder.From(SampleListData) 250 | .WithFormat(ConsoleTableBuilderFormat.Alternative) 251 | .ExportAndWriteLine(); 252 | 253 | _____________________________PrintDemoDivider(); 254 | 255 | Console.WriteLine("From [List] type and MarkDown format w/ custom column name:"); 256 | ConsoleTableBuilder.From(SampleListData) 257 | .WithFormat(ConsoleTableBuilderFormat.MarkDown) 258 | .WithColumn(new List { "N A M E", "[Position]", "Office", "", "Something else I don't care" }) 259 | .ExportAndWriteLine(); 260 | 261 | _____________________________PrintDemoDivider(); 262 | 263 | Console.WriteLine("From [List] (where T:class) type and Minimal format:"); 264 | ConsoleTableBuilder.From(SampleEmployeesList).WithFormat(ConsoleTableBuilderFormat.Minimal).ExportAndWriteLine(); 265 | 266 | _____________________________PrintDemoDivider(); 267 | 268 | Console.WriteLine("From [List] (where T: !class) type and Alternative format:"); 269 | ConsoleTableBuilder.From(new List { 1, 2, 3, 4, 5, 6 }).WithFormat(ConsoleTableBuilderFormat.Alternative).WithColumn("I'm a custom name").ExportAndWrite(); 270 | 271 | 272 | _____________________________PrintDemoDivider(); 273 | 274 | ConsoleTableBuilder.From(new List 275 | { 276 | new object[] { "luong", "son", "ba", null, "phim", null, null, null, 2, null}, 277 | new object[] { "chuc", "anh", "dai", "nhac", null, null, null } 278 | }) 279 | .TrimColumn(true) 280 | .AddRow(new List { 1, "this", "is", "new", "row", "use", "", null, null, null }) 281 | .AddRow(new object[] { "2", "new row", "use", "array[] values", null, null }) 282 | .WithMetadataRow(MetaRowPositions.Top, b => string.Format("=> First top line <{0}>", "FIRST")) 283 | .WithMetadataRow(MetaRowPositions.Top, b => string.Format("=> Second top line <{0}>", "SECOND")) 284 | .WithMetadataRow( 285 | MetaRowPositions.Bottom, 286 | b => string.Format("=> This table has {3} rows and {2} columns=> [{0}] - [test value {1}]", 287 | "test value 1", 288 | 2, 289 | b.NumberOfColumns, 290 | b.NumberOfRows 291 | ) 292 | ) 293 | .WithMetadataRow(MetaRowPositions.Bottom, b => string.Format("=> Bottom line <{0}>", "HELLO WORLD")) 294 | .WithColumn(new List { "THIS", "IS", "ADVANCED", "OPTIONS" }) 295 | .WithCharMapDefinition(new Dictionary { 296 | { CharMapPositions.BorderLeft, '¡' }, 297 | { CharMapPositions.BorderRight, '¡' }, 298 | { CharMapPositions.DividerY, '¡' } 299 | }) 300 | .WithHeaderCharMapDefinition(new Dictionary { 301 | { HeaderCharMapPositions.BottomLeft, '»' }, 302 | { HeaderCharMapPositions.BottomCenter, '»' }, 303 | { HeaderCharMapPositions.BottomRight, '»' }, 304 | { HeaderCharMapPositions.Divider, '¡' }, 305 | { HeaderCharMapPositions.BorderBottom, '»' }, 306 | { HeaderCharMapPositions.BorderLeft, '¡' }, 307 | { HeaderCharMapPositions.BorderRight, '¡' } 308 | }) 309 | .ExportAndWriteLine(); 310 | 311 | Console.ReadKey(); 312 | } 313 | 314 | static DataTable SampleTableData() 315 | { 316 | DataTable table = new DataTable(); 317 | table.Columns.Add("Name", typeof(string)); 318 | table.Columns.Add("Position", typeof(string)); 319 | table.Columns.Add("Office", typeof(string)); 320 | table.Columns.Add("Age", typeof(int)); 321 | table.Columns.Add("Start Date", typeof(DateTime)); 322 | 323 | table.Rows.Add("Airi Satou", "Accountant", "Tokyo", 33, new DateTime(2017, 05, 09)); 324 | table.Rows.Add("Angelica Ramos", "Chief Executive Officer (CEO)", "New York", 47, new DateTime(2017, 01, 12)); 325 | table.Rows.Add("Ashton Cox", "Junior Technical Author", "London", 46, new DateTime(2017, 04, 02)); 326 | table.Rows.Add("Bradley Greer", "Software Engineer", "San Francisco", 28, new DateTime(2017, 11, 15)); 327 | 328 | return table; 329 | } 330 | 331 | static List> SampleShortListData = new List> 332 | { 333 | new List{ ""}, 334 | new List{ ""}, 335 | new List{ ""}, 336 | new List{ ""}, 337 | }; 338 | 339 | static List> SampleListData = new List> 340 | { 341 | new List{ "Sakura Yamamoto", "Support Engineer", "London", 46}, 342 | new List{ "Serge Baldwin", "Data Coordinator", "San Francisco", 28, "something else" }, 343 | new List{ "Shad Decker", "Regional Director", "Edinburgh"}, 344 | }; 345 | 346 | static List SampleEmployeesList = new List 347 | { 348 | new Employee("Airi Satou", "Accountant", "Tokyo", 33, new DateTime(2017, 05, 09)), 349 | new Employee("Angelica Ramos", "Chief Executive Officer (CEO)", "New York", 47, new DateTime(2017, 01, 12)), 350 | new Employee("Ashton Cox", "Junior Technical Author", "London", 46, new DateTime(2017, 04, 02)), 351 | new Employee("Bradley Greer", "Software Engineer", "San Francisco", 28, new DateTime(2017, 11, 15)) 352 | }; 353 | 354 | private class Employee 355 | { 356 | public Employee(string name, string position, string office, int age, DateTime startDate) 357 | { 358 | Name = name; 359 | Position = position; 360 | Office = office; 361 | Age = age; 362 | StartDate = startDate; 363 | } 364 | 365 | [Description("N - A - M - E")] 366 | public string Name { get; set; } 367 | public string Position { get; set; } 368 | public string Office { get; set; } 369 | public int Age { get; set; } 370 | public DateTime StartDate { get; set; } 371 | } 372 | 373 | private static void _____________________________PrintDemoDivider() 374 | { 375 | Console.WriteLine(); 376 | Console.WriteLine(); 377 | // Console.Write(string.Format("\n\n{0}\n", Enumerable.Range(0, Console.WindowWidth).Select(x => "=").Aggregate((s, a) => s + a))); 378 | } 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /Src/ConsoleTableApp/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("ConsoleTableApp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ConsoleTableApp")] 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("c6d28db6-cda1-470d-97c2-1a75279bd185")] 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 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt.Tests/ConsoleTableExt.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Debug 8 | AnyCPU 9 | {8E7D35C2-3170-42CA-86D2-A5CEFBCE3094} 10 | Library 11 | Properties 12 | ConsoleTableExt.Tests 13 | ConsoleTableExt.Tests 14 | v3.5 15 | 512 16 | true 17 | 18 | 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | ..\..\packages\NUnit.3.13.1\lib\net35\nunit.framework.dll 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | {ab7bc904-d18d-4033-9625-e80877d2f94e} 60 | ConsoleTableExt 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt.Tests/CustomFormats.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.ComponentModel; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | 7 | namespace ConsoleTableExt.Tests 8 | { 9 | [TestFixture] 10 | public class CustomFormats 11 | { 12 | public CustomFormats() 13 | { 14 | //set CultureInfo default en-US 15 | System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo("en-US"); 16 | System.Threading.Thread.CurrentThread.CurrentCulture = ci; 17 | System.Threading.Thread.CurrentThread.CurrentUICulture = ci; 18 | } 19 | 20 | [Test] 21 | public void DefaultFormatWithDataTable() 22 | { 23 | var strBuilder = 24 | ConsoleTableBuilder 25 | .From(SampleData.SampleTableData) 26 | .WithTitle("TITLE") 27 | .WithPaddingLeft(String.Empty) 28 | .WithCharMapDefinition() 29 | .Export(); 30 | 31 | Assert.IsTrue(strBuilder.ToString() == @" TITLE 32 | Name Position Office Age Start Date 33 | Airi Satou Accountant Tokyo 33 5/9/2017 12:00:00 AM 34 | Angelica Ramos Chief Executive Officer (CEO) New York 47 1/12/2017 12:00:00 AM 35 | Ashton Cox Junior Technical Author London 46 4/2/2017 12:00:00 AM 36 | Bradley Greer Software Engineer San Francisco 28 11/15/2017 12:00:00 AM 37 | "); 38 | var lines = strBuilder.ToString().Split('\n'); 39 | Assert.IsTrue(lines.Length == 7); 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt.Tests/DefaultFormats.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | 6 | namespace ConsoleTableExt.Tests 7 | { 8 | [TestFixture] 9 | public class DefaultFormats 10 | { 11 | public DefaultFormats() 12 | { 13 | //set CultureInfo default en-US 14 | System.Globalization.CultureInfo ci = new System.Globalization.CultureInfo("en-US"); 15 | System.Threading.Thread.CurrentThread.CurrentCulture = ci; 16 | System.Threading.Thread.CurrentThread.CurrentUICulture = ci; 17 | } 18 | 19 | [Test] 20 | public void DefaultFormatWithDataTable() 21 | { 22 | var strBuilder = 23 | ConsoleTableBuilder 24 | .From(SampleData.SampleTableData) 25 | .Export(); 26 | 27 | TestContext.Out.WriteLine(strBuilder.ToString()); 28 | Assert.IsTrue(strBuilder.ToString() == @" 29 | ------------------------------------------------------------------------------------------------- 30 | | Name | Position | Office | Age | Start Date | 31 | ------------------------------------------------------------------------------------------------- 32 | | Airi Satou | Accountant | Tokyo | 33 | 5/9/2017 12:00:00 AM | 33 | ------------------------------------------------------------------------------------------------- 34 | | Angelica Ramos | Chief Executive Officer (CEO) | New York | 47 | 1/12/2017 12:00:00 AM | 35 | ------------------------------------------------------------------------------------------------- 36 | | Ashton Cox | Junior Technical Author | London | 46 | 4/2/2017 12:00:00 AM | 37 | ------------------------------------------------------------------------------------------------- 38 | | Bradley Greer | Software Engineer | San Francisco | 28 | 11/15/2017 12:00:00 AM | 39 | ------------------------------------------------------------------------------------------------- 40 | ".TrimStart()); 41 | 42 | var lines = strBuilder.ToString().Split('\n'); 43 | Assert.IsTrue(lines.Length == 12); 44 | } 45 | 46 | [Test] 47 | public void MinimalFormatWithDataTable() 48 | { 49 | var strBuilder = 50 | ConsoleTableBuilder 51 | .From(SampleData.SampleTableData) 52 | .WithFormat(ConsoleTableBuilderFormat.Minimal) 53 | .Export(); 54 | 55 | Assert.IsTrue(strBuilder.ToString() == @"Name Position Office Age Start Date 56 | -------------------------------------------------------------------------------------- 57 | Airi Satou Accountant Tokyo 33 5/9/2017 12:00:00 AM 58 | Angelica Ramos Chief Executive Officer (CEO) New York 47 1/12/2017 12:00:00 AM 59 | Ashton Cox Junior Technical Author London 46 4/2/2017 12:00:00 AM 60 | Bradley Greer Software Engineer San Francisco 28 11/15/2017 12:00:00 AM 61 | "); 62 | 63 | var lines = strBuilder.ToString().Split('\n'); 64 | Assert.IsTrue(lines.Length == 7); 65 | } 66 | 67 | [Test] 68 | public void AlternativeFormatWithDataTable() 69 | { 70 | var strBuilder = 71 | ConsoleTableBuilder 72 | .From(SampleData.SampleTableData) 73 | .WithFormat(ConsoleTableBuilderFormat.Alternative) 74 | .Export(); 75 | 76 | Assert.IsTrue(strBuilder.ToString() == @" 77 | +----------------+-------------------------------+---------------+-----+------------------------+ 78 | | Name | Position | Office | Age | Start Date | 79 | +----------------+-------------------------------+---------------+-----+------------------------+ 80 | | Airi Satou | Accountant | Tokyo | 33 | 5/9/2017 12:00:00 AM | 81 | +----------------+-------------------------------+---------------+-----+------------------------+ 82 | | Angelica Ramos | Chief Executive Officer (CEO) | New York | 47 | 1/12/2017 12:00:00 AM | 83 | +----------------+-------------------------------+---------------+-----+------------------------+ 84 | | Ashton Cox | Junior Technical Author | London | 46 | 4/2/2017 12:00:00 AM | 85 | +----------------+-------------------------------+---------------+-----+------------------------+ 86 | | Bradley Greer | Software Engineer | San Francisco | 28 | 11/15/2017 12:00:00 AM | 87 | +----------------+-------------------------------+---------------+-----+------------------------+ 88 | ".TrimStart()); 89 | 90 | var lines = strBuilder.ToString().Split('\n'); 91 | Assert.IsTrue(lines.Length == 12); 92 | } 93 | 94 | [Test] 95 | public void MarkDownFormatWithDataTable() 96 | { 97 | var strBuilder = 98 | ConsoleTableBuilder 99 | .From(SampleData.SampleTableData) 100 | .WithFormat(ConsoleTableBuilderFormat.MarkDown) 101 | .Export(); 102 | 103 | Assert.IsTrue(strBuilder.ToString() == @" 104 | | Name | Position | Office | Age | Start Date | 105 | |----------------|-------------------------------|---------------|-----|------------------------| 106 | | Airi Satou | Accountant | Tokyo | 33 | 5/9/2017 12:00:00 AM | 107 | | Angelica Ramos | Chief Executive Officer (CEO) | New York | 47 | 1/12/2017 12:00:00 AM | 108 | | Ashton Cox | Junior Technical Author | London | 46 | 4/2/2017 12:00:00 AM | 109 | | Bradley Greer | Software Engineer | San Francisco | 28 | 11/15/2017 12:00:00 AM | 110 | ".TrimStart()); 111 | 112 | var lines = strBuilder.ToString().Split('\n'); 113 | Assert.IsTrue(lines.Length == 7); 114 | } 115 | 116 | [Test] 117 | public void AlternativeFormatWithUtf8CharactersDataTable() 118 | { 119 | var strBuilder = 120 | ConsoleTableBuilder 121 | .From(SampleData.SampleListWithUtf8Characters) 122 | .WithTitle("a中123午b", ConsoleColor.Yellow, ConsoleColor.DarkGray) 123 | .WithFormat(ConsoleTableBuilderFormat.Alternative) 124 | .Export(); 125 | 126 | Assert.IsTrue(strBuilder.ToString() == @" 127 | +-------------+-------------- a中123午b +-----------+------+---------+ 128 | | Id | Name | Host | Port | status | 129 | +-------------+-------------------------+-----------+------+---------+ 130 | | 中午午午午c | tab其它语言test | 127.0.0.1 | 80 | success | 131 | +-------------+-------------------------+-----------+------+---------+ 132 | | a中午b | London语a言aa它xx | 127.0.0.1 | 80 | success | 133 | +-------------+-------------------------+-----------+------+---------+ 134 | | Airi Satou | Accountant | 127.0.0.1 | 80 | Tokyo | 135 | +-------------+-------------------------+-----------+------+---------+ 136 | | Ashton Cox | Junior Technical Author | 127.0.0.1 | 80 | success | 137 | +-------------+-------------------------+-----------+------+---------+ 138 | ".TrimStart()); 139 | 140 | var lines = strBuilder.ToString().Split('\n'); 141 | Assert.IsTrue(lines.Length == 12); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt.Tests/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("ConsoleTableExt.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ConsoleTableExt.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 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("8e7d35c2-3170-42ca-86d2-a5cefbce3094")] 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 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt.Tests/SampleData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | 6 | namespace ConsoleTableExt.Tests 7 | { 8 | public class SampleData 9 | { 10 | public static DataTable SampleTableData 11 | { 12 | get 13 | { 14 | DataTable table = new DataTable(); 15 | table.Columns.Add("Name", typeof(string)); 16 | table.Columns.Add("Position", typeof(string)); 17 | table.Columns.Add("Office", typeof(string)); 18 | table.Columns.Add("Age", typeof(int)); 19 | table.Columns.Add("Start Date", typeof(DateTime)); 20 | 21 | table.Rows.Add("Airi Satou", "Accountant", "Tokyo", 33, new DateTime(2017, 05, 09)); 22 | table.Rows.Add("Angelica Ramos", "Chief Executive Officer (CEO)", "New York", 47, new DateTime(2017, 01, 12)); 23 | table.Rows.Add("Ashton Cox", "Junior Technical Author", "London", 46, new DateTime(2017, 04, 02)); 24 | table.Rows.Add("Bradley Greer", "Software Engineer", "San Francisco", 28, new DateTime(2017, 11, 15)); 25 | 26 | return table; 27 | } 28 | } 29 | 30 | public static List> SampleShortListData = new List> 31 | { 32 | new List{ ""}, 33 | new List{ ""}, 34 | new List{ ""}, 35 | new List{ ""}, 36 | }; 37 | 38 | public static List> SampleListData = new List> 39 | { 40 | new List{ "Sakura Yamamoto", "Support Engineer", "London", 46}, 41 | new List{ "Serge Baldwin", "Data Coordinator", "San Francisco", 28, "something else" }, 42 | new List{ "Shad Decker", "Regional Director", "Edinburgh"}, 43 | }; 44 | 45 | public static List SampleEmployeesList = new List 46 | { 47 | new Employee("Airi Satou", "Accountant", "Tokyo", 33, new DateTime(2017, 05, 09)), 48 | new Employee("Angelica Ramos", "Chief Executive Officer (CEO)", "New York", 47, new DateTime(2017, 01, 12)), 49 | new Employee("Ashton Cox", "Junior Technical Author", "London", 46, new DateTime(2017, 04, 02)), 50 | new Employee("Bradley Greer", "Software Engineer", "San Francisco", 28, new DateTime(2017, 11, 15)) 51 | }; 52 | 53 | public static List SampleListWithUtf8Characters = new List() { 54 | new { Id="中午午午午c", Name="tab其它语言test", Host="127.0.0.1", Port=80, status ="success" } , 55 | new { Id="a中午b", Name="London语a言aa它xx", Host="127.0.0.1", Port=80, status ="success" }, 56 | new { Id="Airi Satou", Name="Accountant", Host="127.0.0.1", Port=80, status ="Tokyo" }, 57 | new { Id="Ashton Cox", Name="Junior Technical Author", Host="127.0.0.1", Port=80, status ="success" } 58 | }; 59 | 60 | public class Employee 61 | { 62 | public Employee(string name, string position, string office, int age, DateTime startDate) 63 | { 64 | Name = name; 65 | Position = position; 66 | Office = office; 67 | Age = age; 68 | StartDate = startDate; 69 | } 70 | 71 | [Description("N - A - M - E")] 72 | public string Name { get; set; } 73 | public string Position { get; set; } 74 | public string Office { get; set; } 75 | public int Age { get; set; } 76 | public DateTime StartDate { get; set; } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/CharMapDefinition.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ConsoleTableExt 4 | { 5 | public class CharMapDefinition 6 | { 7 | public static Dictionary FramePipDefinition = new Dictionary 8 | { 9 | { CharMapPositions.TopLeft, '┌' }, 10 | { CharMapPositions.TopCenter, '┬' }, 11 | { CharMapPositions.TopRight, '┐' }, 12 | { CharMapPositions.MiddleLeft, '├' }, 13 | { CharMapPositions.MiddleCenter, '┼' }, 14 | { CharMapPositions.MiddleRight, '┤' }, 15 | { CharMapPositions.BottomLeft, '└' }, 16 | { CharMapPositions.BottomCenter, '┴' }, 17 | { CharMapPositions.BottomRight, '┘' }, 18 | { CharMapPositions.BorderLeft, '│' }, 19 | { CharMapPositions.BorderRight, '│' }, 20 | { CharMapPositions.BorderTop, '─' }, 21 | { CharMapPositions.BorderBottom, '─' }, 22 | { CharMapPositions.DividerY, '│' }, 23 | { CharMapPositions.DividerX, '─' }, 24 | }; 25 | 26 | public static Dictionary FrameDoublePipDefinition = new Dictionary 27 | { 28 | { CharMapPositions.TopLeft, '╔' }, 29 | { CharMapPositions.TopCenter, '╤' }, 30 | { CharMapPositions.TopRight, '╗' }, 31 | { CharMapPositions.MiddleLeft, '╟' }, 32 | { CharMapPositions.MiddleCenter, '┼' }, 33 | { CharMapPositions.MiddleRight, '╢' }, 34 | { CharMapPositions.BottomLeft, '╚' }, 35 | { CharMapPositions.BottomCenter, '╧' }, 36 | { CharMapPositions.BottomRight, '╝' }, 37 | { CharMapPositions.BorderLeft, '║' }, 38 | { CharMapPositions.BorderRight, '║' }, 39 | { CharMapPositions.BorderTop, '═' }, 40 | { CharMapPositions.BorderBottom, '═' }, 41 | { CharMapPositions.DividerY, '│' }, 42 | { CharMapPositions.DividerX, '─' } 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/ConsoleColorNullable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ConsoleTableExt 4 | { 5 | public class ConsoleColorNullable 6 | { 7 | public ConsoleColorNullable() 8 | { 9 | 10 | } 11 | 12 | public ConsoleColorNullable(ConsoleColor foregroundColor) 13 | { 14 | ForegroundColor = foregroundColor; 15 | IsForegroundColorNull = false; 16 | } 17 | 18 | public ConsoleColorNullable(ConsoleColor foregroundColor, ConsoleColor backgroundColor) 19 | { 20 | ForegroundColor = foregroundColor; 21 | BackgroundColor = backgroundColor; 22 | IsForegroundColorNull = false; 23 | IsBackgroundColorNull = false; 24 | } 25 | 26 | public bool IsForegroundColorNull = true; 27 | public bool IsBackgroundColorNull = true; 28 | public ConsoleColor ForegroundColor { get; set; } 29 | public ConsoleColor BackgroundColor { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/ConsoleTableBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Linq; 6 | 7 | namespace ConsoleTableExt 8 | { 9 | public class ConsoleTableBaseData 10 | { 11 | public List Column { get; set; } 12 | public List> Rows { get; set; } 13 | } 14 | 15 | public class ConsoleTableBuilder 16 | { 17 | internal List Column { get; set; } 18 | internal List FormattedColumns { get; set; } 19 | 20 | internal List> Rows { get; set; } 21 | internal List> FormattedRows { get; set; } 22 | 23 | internal ConsoleTableBuilderFormat TableFormat { get; set; } 24 | internal Dictionary CharMapPositionStore { get; set; } = null; 25 | internal Dictionary HeaderCharMapPositionStore { get; set; } = null; 26 | internal List>> TopMetadataRows = new List>>(); 27 | internal List>> BottomMetadataRows = new List>>(); 28 | internal Dictionary TextAligmentData = new Dictionary(); 29 | internal Dictionary HeaderTextAligmentData = new Dictionary(); 30 | internal Dictionary MinLengthData = new Dictionary(); 31 | 32 | internal bool CanTrimColumn = false; 33 | internal string TableTitle = string.Empty; 34 | internal TextAligntment TableTitleTextAlignment = TextAligntment.Center; 35 | internal ConsoleColorNullable TableTitleColor = new ConsoleColorNullable(); 36 | internal string PaddingLeft = " "; 37 | internal string PaddingRight = " "; 38 | 39 | internal int TitlePositionStartAt { get; set; } 40 | internal int TitlePositionLength { get; set; } 41 | 42 | internal Dictionary> FormatterStore = new Dictionary>(); 43 | internal Dictionary> ColumnFormatterStore = new Dictionary>(); 44 | 45 | private ConsoleTableBuilder() 46 | { 47 | Column = new List(); 48 | Rows = new List>(); 49 | TableFormat = ConsoleTableBuilderFormat.Default; 50 | } 51 | 52 | /// 53 | /// This function allow developer implement themeself data-source 54 | /// 55 | /// 56 | /// 57 | public static ConsoleTableBuilder From(Func func) 58 | { 59 | if (func != null) 60 | { 61 | var baseData = func.Invoke(); 62 | return From(baseData); 63 | } 64 | else 65 | { 66 | throw new Exception("invaild function"); 67 | } 68 | } 69 | 70 | public static ConsoleTableBuilder From(ConsoleTableBaseData baseData) 71 | { 72 | var builder = new ConsoleTableBuilder(); 73 | 74 | if (baseData != null) 75 | { 76 | if (baseData.Rows != null) 77 | { 78 | builder.Rows = baseData.Rows; 79 | } 80 | 81 | if (baseData.Column != null) 82 | { 83 | builder.Column = baseData.Column; 84 | } 85 | } 86 | 87 | return builder; 88 | } 89 | 90 | public static ConsoleTableBuilder From(List list) 91 | { 92 | var builder = new ConsoleTableBuilder(); 93 | foreach (var value in list) 94 | { 95 | builder.Rows.Add(new List { value }); 96 | } 97 | 98 | return builder; 99 | } 100 | 101 | public static ConsoleTableBuilder From(List list) 102 | { 103 | var builder = new ConsoleTableBuilder(); 104 | foreach (var value in list) 105 | { 106 | builder.Rows.Add(new List { value }); 107 | } 108 | 109 | return builder; 110 | } 111 | 112 | public static ConsoleTableBuilder From(List list) 113 | { 114 | var builder = new ConsoleTableBuilder(); 115 | 116 | foreach (var value in list) 117 | { 118 | builder.Rows.Add(new List { value }); 119 | } 120 | 121 | return builder; 122 | } 123 | 124 | public static ConsoleTableBuilder From(DataTable dt) 125 | { 126 | var builder = new ConsoleTableBuilder(); 127 | 128 | if (dt == null) 129 | { 130 | return builder; 131 | } 132 | 133 | var columnNames = dt.Columns.Cast() 134 | .Select(x => x.ColumnName) 135 | .ToList(); 136 | #if NET35 137 | columnNames.ForEach(f => builder.Column.Add(f)); 138 | #else 139 | 140 | builder.Column = new List(columnNames); 141 | #endif 142 | foreach (DataRow row in dt.Rows) 143 | { 144 | builder.Rows.Add(new List(row.ItemArray)); 145 | } 146 | 147 | return builder; 148 | } 149 | 150 | //public static ConsoleTableBuilder From(List> list) 151 | //{ 152 | // var builder = new ConsoleTableBuilder(); 153 | // if (list == null) 154 | // { 155 | // return builder; 156 | // } 157 | 158 | // foreach (var row in list) 159 | // { 160 | // builder.Rows.Add(row.Select(x => (object)x).ToList()); 161 | // } 162 | 163 | // return builder; 164 | //} 165 | 166 | //public static ConsoleTableBuilder From(List> list) where T : sbyte, byte, short, ushort, int, uint, long, ulong, float, double, decimal, bool, char 167 | //{ 168 | // var builder = new ConsoleTableBuilder(); 169 | // if (list == null) 170 | // { 171 | // return builder; 172 | // } 173 | 174 | // foreach (var row in list) 175 | // { 176 | // builder.Rows.Add(row.Select(x => (object)x).ToList()); 177 | // } 178 | 179 | // return builder; 180 | //} 181 | 182 | public static ConsoleTableBuilder From(List list) where T : class 183 | { 184 | var builder = new ConsoleTableBuilder(); 185 | 186 | var genericAgrs = typeof(T).GetGenericArguments(); 187 | if (genericAgrs.Count() > 0) 188 | { 189 | var elementType = genericAgrs[0]; 190 | var typeCode = Type.GetTypeCode(elementType); 191 | 192 | if (typeCode != TypeCode.Object && typeCode != TypeCode.DBNull) 193 | { 194 | // T is a List of a primitive type (e.g. List, List, etc.) 195 | 196 | 197 | if (list == null) 198 | { 199 | return builder; 200 | } 201 | 202 | //var objectList = new List>(); 203 | 204 | foreach (var item in list) 205 | { 206 | List row = new List(); 207 | foreach (var element in (IEnumerable)item) 208 | { 209 | row.Add(element); 210 | } 211 | //objectList.Add(row); 212 | 213 | builder.Rows.Add(row); 214 | } 215 | 216 | return builder; 217 | } 218 | } 219 | 220 | // T is a List of a non-primitive type (e.g. List, etc.) 221 | if (list == null) 222 | { 223 | return builder; 224 | } 225 | 226 | var isClass = typeof(T).IsClass; 227 | var props = new List(); 228 | 229 | if (list.Any()) 230 | { 231 | props = list.First().GetType().GetProperties().ToList(); 232 | } 233 | 234 | List columnNames; 235 | if (isClass) 236 | { 237 | columnNames = props.Select(p => 238 | { 239 | object[] attrs = p.GetCustomAttributes(true); 240 | foreach (object attr in attrs) 241 | { 242 | if (attr is System.ComponentModel.DescriptionAttribute) 243 | { 244 | return ((System.ComponentModel.DescriptionAttribute)attr).Description; 245 | } 246 | } 247 | 248 | return p.Name as object; 249 | }).ToList() ?? new List(); 250 | } 251 | else 252 | { 253 | columnNames = new List { "Value" }; 254 | } 255 | 256 | 257 | builder.Column = columnNames; 258 | 259 | foreach (var item in list) 260 | { 261 | if (isClass == true) 262 | { 263 | var itemPropValues = new List(); 264 | 265 | foreach (var prop in props) 266 | { 267 | #if NET35 268 | var objValue = prop.GetValue(item, new object[]{ }); 269 | #else 270 | var objValue = prop.GetValue(item); 271 | #endif 272 | itemPropValues.Add(objValue); 273 | } 274 | 275 | builder.Rows.Add(itemPropValues); 276 | } 277 | else 278 | { 279 | builder.Rows.Add(new List { item }); 280 | } 281 | } 282 | 283 | return builder; 284 | } 285 | 286 | public static ConsoleTableBuilder From(List rows) 287 | { 288 | var builder = new ConsoleTableBuilder(); 289 | 290 | if (rows == null) 291 | { 292 | return builder; 293 | } 294 | 295 | foreach (var row in rows) 296 | { 297 | builder.Rows.Add(new List(row)); 298 | } 299 | 300 | return builder; 301 | } 302 | 303 | public static ConsoleTableBuilder From(List> rows) 304 | { 305 | var builder = new ConsoleTableBuilder(); 306 | 307 | if (rows == null) 308 | { 309 | return builder; 310 | } 311 | 312 | foreach (var row in rows) 313 | { 314 | builder.Rows.Add(row); 315 | } 316 | 317 | return builder; 318 | } 319 | 320 | internal void PopulateFormattedColumnsRows() 321 | { 322 | FormattedColumns = Enumerable.Range(0, Column.Count) 323 | .Select(idx => 324 | { 325 | if (ColumnFormatterStore.ContainsKey(idx)) 326 | { 327 | return ColumnFormatterStore[idx](Column[idx] == null ? string.Empty : Column[idx].ToString()); 328 | } 329 | else 330 | { 331 | return Column[idx] == null ? string.Empty : Column[idx].ToString(); 332 | } 333 | }).ToList(); 334 | 335 | 336 | FormattedRows = new List>(); 337 | for (int i = 0; i < Rows.Count; i++) 338 | { 339 | FormattedRows.Add( 340 | Enumerable.Range(0, Rows[i].Count) 341 | .Select(idx => 342 | { 343 | if (FormatterStore.ContainsKey(idx)) 344 | { 345 | return FormatterStore[idx](Rows[i][idx] == null ? string.Empty : Rows[i][idx]); 346 | } 347 | else 348 | { 349 | return Rows[i][idx]; 350 | } 351 | }).ToList()); 352 | } 353 | } 354 | 355 | internal void CenterRowContent(List columnLengths) 356 | { 357 | for (int i = 0; i < FormattedRows.Count; i++) 358 | { 359 | for (int j = 0; j < FormattedRows[i].Count; j++) 360 | { 361 | if (TextAligmentData.ContainsKey(j) && TextAligmentData[j] == TextAligntment.Center) 362 | { 363 | FormattedRows[i][j] = CenteredString(FormattedRows[i][j], columnLengths[j]); 364 | } 365 | } 366 | } 367 | } 368 | 369 | internal string[] CenterColumnContent(string[] columnSlices, List columnLengths) 370 | { 371 | for (int i = 0; i < columnSlices.Length; i++) 372 | { 373 | if (HeaderTextAligmentData.ContainsKey(i)) 374 | { 375 | if (HeaderTextAligmentData[i] == TextAligntment.Center) 376 | { 377 | columnSlices[i] = CenteredString(columnSlices[i], columnLengths[i]); 378 | } 379 | } 380 | else 381 | { 382 | if (TextAligmentData.ContainsKey(i) && TextAligmentData[i] == TextAligntment.Center) 383 | { 384 | columnSlices[i] = CenteredString(columnSlices[i], columnLengths[i]); 385 | } 386 | } 387 | } 388 | 389 | return columnSlices; 390 | } 391 | 392 | private string CenteredString(object s, int width) 393 | { 394 | if (s == null) 395 | { 396 | return null; 397 | } 398 | 399 | if (s.ToString().Length >= width) 400 | { 401 | return s.ToString(); 402 | } 403 | 404 | int leftPadding = (width - s.ToString().Length) / 2; 405 | int rightPadding = width - s.ToString().Length - leftPadding; 406 | 407 | return new string(' ', leftPadding) + s + new string(' ', rightPadding); 408 | } 409 | 410 | internal List GetCadidateColumnLengths(bool withUtf8Characters = true) 411 | { 412 | var columnLengths = new List(); 413 | 414 | var numberOfColumns = 0; 415 | if (FormattedRows.Any()) 416 | { 417 | numberOfColumns = FormattedRows.Max(x => x.Count); 418 | } 419 | else 420 | { 421 | if (FormattedColumns != null) 422 | { 423 | numberOfColumns = FormattedColumns.Count; 424 | } 425 | } 426 | 427 | if (numberOfColumns == 0) 428 | { 429 | return new List(); 430 | } 431 | 432 | if (numberOfColumns < FormattedColumns.Count) 433 | { 434 | numberOfColumns = FormattedColumns.Count; 435 | } 436 | 437 | for (var i = 0; i < numberOfColumns; i++) 438 | { 439 | var maxRow = 0; 440 | if (FormattedRows.Any()) 441 | { 442 | maxRow = FormattedRows 443 | .Where(x => i < x.Count) 444 | .Select(x => x[i]) // list cells of column i 445 | .Max(x => x == null ? 0 : x.ToString().RealLength(withUtf8Characters)); 446 | } 447 | 448 | if (FormattedColumns.ToArray().Length > i && (FormattedColumns[i] ?? string.Empty).ToString().RealLength(withUtf8Characters) > maxRow) 449 | { 450 | maxRow = FormattedColumns[i].ToString().RealLength(withUtf8Characters); 451 | } 452 | 453 | if (MinLengthData != null && MinLengthData.ContainsKey(i)) 454 | { 455 | columnLengths.Add(maxRow > MinLengthData[i] ? maxRow : MinLengthData[i]); 456 | } 457 | else 458 | { 459 | columnLengths.Add(maxRow); 460 | } 461 | } 462 | 463 | //if (!columnLengths.Any()) 464 | //{ 465 | // throw new Exception("Table has no columns"); 466 | //} 467 | 468 | if (this.CanTrimColumn) 469 | { 470 | if (columnLengths.Any()) 471 | { 472 | var temp = columnLengths; 473 | 474 | //for (int i = 0; i < temp.Count; i++) 475 | //{ 476 | // if (temp[i] == 0) 477 | // { 478 | // columnLengths.RemoveAt(0); 479 | // } 480 | // else 481 | // { 482 | // break; 483 | // } 484 | //} 485 | 486 | for (int i = temp.Count - 1; i >= 0; i--) 487 | { 488 | if (temp[i] == 0) 489 | { 490 | columnLengths.RemoveAt(i); 491 | } 492 | else 493 | { 494 | break; 495 | } 496 | } 497 | } 498 | } 499 | 500 | return columnLengths; 501 | } 502 | 503 | public int NumberOfColumns { get { return this.GetCadidateColumnLengths().Count; } } 504 | public int NumberOfRows { get { return this.Rows.Count; } } 505 | 506 | //internal string Format(char delimiter) 507 | //{ 508 | // string delim = delimiter == '\0' ? string.Empty : delimiter.ToString(); 509 | 510 | // var columnLengths = GetCadidateColumnLengths(); 511 | 512 | // // | {0,-14} | {1,-29} | {2,-13} | {3,-3} | {4,-22} | 513 | // if (columnLengths.Count > 0) 514 | // { 515 | // var format = Enumerable.Range(0, columnLengths.Count) 516 | // .Select(i => PaddingLeft + "{" + i + "," + (TextAligmentData == null ? "-" : (TextAligmentData.ContainsKey(i) ? TextAligmentData[i].ToString() : "-")) + columnLengths[i] + "}" + PaddingRight) 517 | // .Aggregate((s, a) => s + delim + a); 518 | 519 | // return delim + format + delim; 520 | // } 521 | // else 522 | // { 523 | // return string.Empty; 524 | // } 525 | //} 526 | 527 | private string EmbedTitle(string line) 528 | { 529 | var originalTitleLength = TableTitle.Length; 530 | 531 | if (!string.IsNullOrEmpty(TableTitle) && TableTitle.Trim().Length > 0) // !IsNullOrWhiteSpace 532 | { 533 | if (TableTitle.Length > line.Length - 4) 534 | { 535 | TableTitle = TableTitle.Substring(0, line.Length - 4); 536 | 537 | if (originalTitleLength != TableTitle.Length && TableTitle.Length > 3) 538 | { 539 | TableTitle = TableTitle.Substring(0, TableTitle.Length - 3) + "..."; 540 | } 541 | } 542 | 543 | TableTitle = TableTitle.Trim(); 544 | TableTitle = " " + TableTitle + " "; 545 | var startPoint = 0; 546 | switch (TableTitleTextAlignment) 547 | { 548 | case TextAligntment.Left: 549 | startPoint = 1; 550 | break; 551 | case TextAligntment.Right: 552 | startPoint = line.Length - 1 - TableTitle.RealLength(true); 553 | break; 554 | case TextAligntment.Center: 555 | startPoint = (line.Length - TableTitle.RealLength(true)) / 2; 556 | break; 557 | default: 558 | break; 559 | } 560 | 561 | TitlePositionStartAt = startPoint; 562 | var newBeginTableFormat = line.Substring(0, startPoint); 563 | newBeginTableFormat += TableTitle; 564 | TitlePositionLength = TableTitle.Length; 565 | int reallength = newBeginTableFormat.RealLength(true); 566 | newBeginTableFormat += line.Substring(reallength, line.Length - reallength); 567 | 568 | line = newBeginTableFormat; 569 | line = line.Replace("\0", " "); 570 | } 571 | 572 | return line; 573 | } 574 | #region Table lines 575 | 576 | internal string CreateTableTopLine(List columnLengths, Dictionary definition) 577 | { 578 | var borderTop = definition[CharMapPositions.BorderTop]; 579 | var topLeft = definition[CharMapPositions.TopLeft]; 580 | var topCenter = definition[CharMapPositions.TopCenter]; 581 | var topRight = definition[CharMapPositions.TopRight]; 582 | 583 | if (columnLengths.Count > 0) 584 | { 585 | var result = Enumerable.Range(0, columnLengths.Count) 586 | .Select(i => new string(borderTop, columnLengths[i] + (PaddingLeft + PaddingRight).Length)) 587 | .Aggregate((s, a) => s + (CanRemoveDividerY() ? string.Empty : topCenter.ToString()) + a); 588 | 589 | var line = (CanRemoveBorderLeft() ? string.Empty : topLeft.ToString()) + result + (CanRemoveBorderRight() ? string.Empty : topRight.ToString()); 590 | 591 | line = EmbedTitle(line); 592 | 593 | if (line.Trim('\0').Length == 0) 594 | { 595 | line = string.Empty; 596 | } 597 | 598 | return line; 599 | } 600 | else 601 | { 602 | return string.Empty; 603 | } 604 | } 605 | 606 | internal string CreateTableContentLineFormat(List columnLengths, Dictionary definition) 607 | { 608 | var borderLeft = definition[CharMapPositions.BorderLeft]; 609 | var divider = definition[CharMapPositions.DividerY]; 610 | var borderRight = definition[CharMapPositions.BorderRight]; 611 | 612 | if (columnLengths.Count > 0) 613 | { 614 | var result = Enumerable.Range(0, columnLengths.Count) 615 | .Select(i => 616 | { 617 | var alignmentChar = string.Empty; 618 | if (TextAligmentData == null || !TextAligmentData.ContainsKey(i) || TextAligmentData[i] == TextAligntment.Left) 619 | { 620 | alignmentChar = "-"; 621 | } 622 | return PaddingLeft + "{" + i + "," + alignmentChar + columnLengths[i] + "}" + PaddingRight; 623 | }) 624 | .Aggregate((s, a) => s + (CanRemoveDividerY() ? string.Empty : divider.ToString()) + a); 625 | 626 | var line = (CanRemoveBorderLeft() ? string.Empty : borderLeft.ToString()) + result + (CanRemoveBorderRight() ? string.Empty : borderRight.ToString()); 627 | 628 | return line; 629 | } 630 | else 631 | { 632 | return string.Empty; 633 | } 634 | } 635 | 636 | internal string CreateRawLineFormat(List columnLengths, Dictionary definition, params object[] args) 637 | { 638 | var borderLeft = definition[CharMapPositions.BorderLeft]; 639 | var divider = definition[CharMapPositions.DividerY]; 640 | var borderRight = definition[CharMapPositions.BorderRight]; 641 | 642 | if (columnLengths.Count > 0) 643 | { 644 | var result = Enumerable.Range(0, columnLengths.Count) 645 | .Select(i => 646 | { 647 | var alignmentChar = string.Empty; 648 | if (TextAligmentData == null || !TextAligmentData.ContainsKey(i) || TextAligmentData[i] == TextAligntment.Left) 649 | { 650 | alignmentChar = "-"; 651 | } 652 | if (args.Length > i) 653 | { 654 | string value = args[i]?.ToString() ?? ""; 655 | return PaddingLeft + "{" + i + "," + alignmentChar + (columnLengths[i] - (value.RealLength(true) - value.Length)) + "}" + PaddingRight; 656 | } 657 | else 658 | return PaddingLeft + "{" + i + "," + alignmentChar + columnLengths[i] + "}" + PaddingRight; 659 | 660 | }) 661 | .Aggregate((s, a) => s + (CanRemoveDividerY() ? string.Empty : divider.ToString()) + a); 662 | 663 | var line = (CanRemoveBorderLeft() ? string.Empty : borderLeft.ToString()) + result + (CanRemoveBorderRight() ? string.Empty : borderRight.ToString()); 664 | 665 | return line; 666 | } 667 | else 668 | { 669 | return string.Empty; 670 | } 671 | } 672 | internal string CreateTableMiddleLine(List columnLengths, Dictionary definition) 673 | { 674 | var dividerX = definition[CharMapPositions.DividerX]; 675 | var middleLeft = definition[CharMapPositions.MiddleLeft]; 676 | var middleCenter = definition[CharMapPositions.MiddleCenter]; 677 | var middleRight = definition[CharMapPositions.MiddleRight]; 678 | 679 | if (columnLengths.Count > 0) 680 | { 681 | var result = Enumerable.Range(0, columnLengths.Count) 682 | .Select(i => new string(dividerX, columnLengths[i] + (PaddingLeft + PaddingRight).Length)) 683 | .Aggregate((s, a) => s + (CanRemoveDividerY() ? string.Empty : middleCenter.ToString()) + a); 684 | 685 | var line = (CanRemoveBorderLeft() ? string.Empty : middleLeft.ToString()) + result + (CanRemoveBorderRight() ? string.Empty : middleRight.ToString()); 686 | 687 | if (line.Trim('\0').Length == 0) 688 | { 689 | line = string.Empty; 690 | } 691 | 692 | return line; 693 | } 694 | else 695 | { 696 | return string.Empty; 697 | } 698 | } 699 | 700 | 701 | internal string CreateTableBottomLine(List columnLengths, Dictionary definition) 702 | { 703 | var borderBottom = definition[CharMapPositions.BorderBottom]; 704 | var bottomLeft = definition[CharMapPositions.BottomLeft]; 705 | var bottomCenter = definition[CharMapPositions.BottomCenter]; 706 | var bottomRight = definition[CharMapPositions.BottomRight]; 707 | 708 | if (columnLengths.Count > 0) 709 | { 710 | var result = Enumerable.Range(0, columnLengths.Count) 711 | .Select(i => new string(borderBottom, columnLengths[i] + (PaddingLeft + PaddingRight).Length)) 712 | .Aggregate((s, a) => s + (CanRemoveDividerY() ? string.Empty : bottomCenter.ToString()) + a); 713 | 714 | var line = (CanRemoveBorderLeft() ? string.Empty : bottomLeft.ToString()) + result + (CanRemoveBorderRight() ? string.Empty : bottomRight.ToString()); 715 | 716 | if (line.Trim('\0').Length == 0) 717 | { 718 | line = string.Empty; 719 | } 720 | 721 | return line; 722 | } 723 | else 724 | { 725 | return string.Empty; 726 | } 727 | } 728 | 729 | #endregion 730 | 731 | 732 | #region Header lines 733 | 734 | internal string CreateHeaderTopLine(List columnLengths, Dictionary definition, Dictionary headerDefinition) 735 | { 736 | var borderTop = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.BorderTop) ? headerDefinition[HeaderCharMapPositions.BorderTop] : definition[CharMapPositions.BorderTop]; 737 | var topLeft = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.TopLeft) ? headerDefinition[HeaderCharMapPositions.TopLeft] : definition[CharMapPositions.TopLeft]; 738 | var topCenter = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.TopCenter) ? headerDefinition[HeaderCharMapPositions.TopCenter] : definition[CharMapPositions.TopCenter]; 739 | var topRight = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.TopRight) ? headerDefinition[HeaderCharMapPositions.TopRight] : definition[CharMapPositions.TopRight]; 740 | 741 | if (columnLengths.Count > 0) 742 | { 743 | var result = Enumerable.Range(0, columnLengths.Count) 744 | .Select(i => new string(borderTop, columnLengths[i] + (PaddingLeft + PaddingRight).Length)) 745 | .Aggregate((s, a) => s + (CanRemoveDividerY() ? string.Empty : topCenter.ToString()) + a); 746 | 747 | var line = (CanRemoveBorderLeft() ? string.Empty : topLeft.ToString()) + result + (CanRemoveBorderRight() ? string.Empty : topRight.ToString()); 748 | 749 | line = EmbedTitle(line); 750 | 751 | if (line.Trim('\0').Length == 0) 752 | { 753 | line = string.Empty; 754 | } 755 | 756 | return line; 757 | } 758 | else 759 | { 760 | return string.Empty; 761 | } 762 | } 763 | 764 | internal string CreateHeaderContentLineFormat(List columnLengths, Dictionary definition, Dictionary headerDefinition) 765 | { 766 | var borderLeft = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.BorderLeft) ? headerDefinition[HeaderCharMapPositions.BorderLeft] : definition[CharMapPositions.BorderLeft]; 767 | var divider = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.Divider) ? headerDefinition[HeaderCharMapPositions.Divider] : definition[CharMapPositions.DividerY]; 768 | var borderRight = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.BorderRight) ? headerDefinition[HeaderCharMapPositions.BorderRight] : definition[CharMapPositions.BorderRight]; 769 | 770 | if (columnLengths.Count > 0) 771 | { 772 | var result = Enumerable.Range(0, columnLengths.Count) 773 | .Select(i => 774 | { 775 | var alignmentChar = string.Empty; 776 | 777 | if (HeaderTextAligmentData.ContainsKey(i)) 778 | { 779 | if (HeaderTextAligmentData[i] == TextAligntment.Left) 780 | { 781 | alignmentChar = "-"; 782 | } 783 | } 784 | else 785 | { 786 | if (TextAligmentData == null || !TextAligmentData.ContainsKey(i) || TextAligmentData[i] == TextAligntment.Left) 787 | { 788 | alignmentChar = "-"; 789 | } 790 | } 791 | 792 | return PaddingLeft + "{" + i + "," + alignmentChar + columnLengths[i] + "}" + PaddingRight; 793 | }) 794 | .Aggregate((s, a) => s + (CanRemoveDividerY() ? string.Empty : divider.ToString()) + a); 795 | 796 | var line = (CanRemoveBorderLeft() ? string.Empty : borderLeft.ToString()) + result + (CanRemoveBorderRight() ? string.Empty : borderRight.ToString()); 797 | 798 | return line; 799 | } 800 | else 801 | { 802 | return string.Empty; 803 | } 804 | } 805 | 806 | internal string CreateHeaderBottomLine(List columnLengths, Dictionary definition, Dictionary headerDefinition) 807 | { 808 | var borderBottom = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.BorderBottom) ? headerDefinition[HeaderCharMapPositions.BorderBottom] : definition[CharMapPositions.DividerX]; 809 | var bottomLeft = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.BottomLeft) ? headerDefinition[HeaderCharMapPositions.BottomLeft] : definition[CharMapPositions.MiddleLeft]; 810 | var bottomCenter = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.BottomCenter) ? headerDefinition[HeaderCharMapPositions.BottomCenter] : definition[CharMapPositions.MiddleRight]; 811 | var bottomRight = headerDefinition != null && headerDefinition.ContainsKey(HeaderCharMapPositions.BottomRight) ? headerDefinition[HeaderCharMapPositions.BottomRight] : definition[CharMapPositions.MiddleCenter]; 812 | 813 | if (columnLengths.Count > 0) 814 | { 815 | var result = Enumerable.Range(0, columnLengths.Count) 816 | .Select(i => new string(borderBottom, columnLengths[i] + (PaddingLeft + PaddingRight).Length)) 817 | .Aggregate((s, a) => s + (CanRemoveDividerY() ? string.Empty : bottomCenter.ToString()) + a); 818 | 819 | var line = (CanRemoveBorderLeft() ? string.Empty : bottomLeft.ToString()) + result + (CanRemoveBorderRight() ? string.Empty : bottomRight.ToString()); 820 | 821 | if (line.Trim('\0').Length == 0) 822 | { 823 | line = string.Empty; 824 | } 825 | 826 | return line; 827 | } 828 | else 829 | { 830 | return string.Empty; 831 | } 832 | } 833 | 834 | #endregion 835 | 836 | internal bool CanRemoveBorderLeft() 837 | { 838 | if (HeaderCharMapPositionStore == null) 839 | { 840 | return new List { 841 | CharMapPositionStore[CharMapPositions.TopLeft], 842 | CharMapPositionStore[CharMapPositions.MiddleLeft], 843 | CharMapPositionStore[CharMapPositions.BottomLeft], 844 | CharMapPositionStore[CharMapPositions.BorderLeft] 845 | } 846 | .Select(x => x.ToString()) 847 | .Aggregate((s, a) => s + a) 848 | .Replace("\0", string.Empty) 849 | .Trim().Length == 0; 850 | } 851 | else 852 | { 853 | var data = new List { }; 854 | data.Add(HeaderCharMapPositionStore.ContainsKey(HeaderCharMapPositions.TopLeft) ? 855 | HeaderCharMapPositionStore[HeaderCharMapPositions.TopLeft] : CharMapPositionStore[CharMapPositions.TopLeft]); 856 | 857 | data.Add(HeaderCharMapPositionStore.ContainsKey(HeaderCharMapPositions.BorderLeft) ? 858 | HeaderCharMapPositionStore[HeaderCharMapPositions.BorderLeft] : CharMapPositionStore[CharMapPositions.BorderLeft]); 859 | 860 | data.Add(HeaderCharMapPositionStore.ContainsKey(HeaderCharMapPositions.BottomLeft) ? 861 | HeaderCharMapPositionStore[HeaderCharMapPositions.BottomLeft] : CharMapPositionStore[CharMapPositions.MiddleLeft]); 862 | 863 | data.Add(CharMapPositionStore[CharMapPositions.MiddleLeft]); 864 | data.Add(CharMapPositionStore[CharMapPositions.BorderLeft]); 865 | data.Add(CharMapPositionStore[CharMapPositions.BottomLeft]); 866 | 867 | return 868 | data 869 | .Select(x => x.ToString()) 870 | .Aggregate((s, a) => s + a) 871 | .Replace("\0", string.Empty) 872 | .Trim().Length == 0; 873 | } 874 | } 875 | 876 | internal bool CanRemoveBorderRight() 877 | { 878 | if (HeaderCharMapPositionStore == null) 879 | { 880 | return new List { 881 | CharMapPositionStore[CharMapPositions.TopRight], 882 | CharMapPositionStore[CharMapPositions.MiddleRight], 883 | CharMapPositionStore[CharMapPositions.BottomRight], 884 | CharMapPositionStore[CharMapPositions.BorderRight] 885 | } 886 | .Select(x => x.ToString()) 887 | .Aggregate((s, a) => s + a) 888 | .Replace("\0", string.Empty) 889 | .Trim().Length == 0; 890 | } 891 | else 892 | { 893 | var data = new List { }; 894 | data.Add(HeaderCharMapPositionStore.ContainsKey(HeaderCharMapPositions.TopRight) ? 895 | HeaderCharMapPositionStore[HeaderCharMapPositions.TopRight] : CharMapPositionStore[CharMapPositions.TopRight]); 896 | 897 | data.Add(HeaderCharMapPositionStore.ContainsKey(HeaderCharMapPositions.BorderRight) ? 898 | HeaderCharMapPositionStore[HeaderCharMapPositions.BorderRight] : CharMapPositionStore[CharMapPositions.BorderRight]); 899 | 900 | data.Add(HeaderCharMapPositionStore.ContainsKey(HeaderCharMapPositions.BottomRight) ? 901 | HeaderCharMapPositionStore[HeaderCharMapPositions.BottomRight] : CharMapPositionStore[CharMapPositions.MiddleRight]); 902 | 903 | data.Add(CharMapPositionStore[CharMapPositions.MiddleRight]); 904 | data.Add(CharMapPositionStore[CharMapPositions.BorderRight]); 905 | data.Add(CharMapPositionStore[CharMapPositions.BottomRight]); 906 | 907 | return 908 | data 909 | .Select(x => x.ToString()) 910 | .Aggregate((s, a) => s + a) 911 | .Replace("\0", string.Empty) 912 | .Trim().Length == 0; 913 | } 914 | } 915 | 916 | internal bool CanRemoveDividerY() 917 | { 918 | if (HeaderCharMapPositionStore == null) 919 | { 920 | return new List { 921 | CharMapPositionStore[CharMapPositions.TopCenter], 922 | CharMapPositionStore[CharMapPositions.MiddleCenter], 923 | CharMapPositionStore[CharMapPositions.BottomCenter], 924 | CharMapPositionStore[CharMapPositions.DividerY] 925 | } 926 | .Select(x => x.ToString()) 927 | .Aggregate((s, a) => s + a) 928 | .Replace("\0", string.Empty) 929 | .Trim().Length == 0; 930 | } 931 | else 932 | { 933 | var data = new List { }; 934 | data.Add(HeaderCharMapPositionStore.ContainsKey(HeaderCharMapPositions.TopCenter) ? 935 | HeaderCharMapPositionStore[HeaderCharMapPositions.TopCenter] : CharMapPositionStore[CharMapPositions.TopCenter]); 936 | 937 | data.Add(HeaderCharMapPositionStore.ContainsKey(HeaderCharMapPositions.Divider) ? 938 | HeaderCharMapPositionStore[HeaderCharMapPositions.Divider] : CharMapPositionStore[CharMapPositions.DividerY]); 939 | 940 | data.Add(HeaderCharMapPositionStore.ContainsKey(HeaderCharMapPositions.BottomCenter) ? 941 | HeaderCharMapPositionStore[HeaderCharMapPositions.BottomCenter] : CharMapPositionStore[CharMapPositions.MiddleCenter]); 942 | 943 | data.Add(CharMapPositionStore[CharMapPositions.MiddleCenter]); 944 | data.Add(CharMapPositionStore[CharMapPositions.DividerY]); 945 | data.Add(CharMapPositionStore[CharMapPositions.BottomCenter]); 946 | 947 | return 948 | data 949 | .Select(x => x.ToString()) 950 | .Aggregate((s, a) => s + a) 951 | .Replace("\0", string.Empty) 952 | .Trim().Length == 0; 953 | } 954 | } 955 | } 956 | } 957 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/ConsoleTableBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace ConsoleTableExt 8 | { 9 | public static class ConsoleTableBuilderExtensions 10 | { 11 | public static ConsoleTableBuilder AddColumn(this ConsoleTableBuilder builder, string columnName) 12 | { 13 | builder.Column.Add(columnName); 14 | return builder; 15 | } 16 | 17 | public static ConsoleTableBuilder AddColumn(this ConsoleTableBuilder builder, List columnNames) 18 | { 19 | #if NET35 20 | columnNames.ForEach(f => builder.Column.Add(f)); 21 | #else 22 | builder.Column.AddRange(columnNames); 23 | #endif 24 | return builder; 25 | } 26 | 27 | public static ConsoleTableBuilder AddColumn(this ConsoleTableBuilder builder, params string[] columnNames) 28 | { 29 | builder.Column.AddRange(new List(columnNames)); 30 | return builder; 31 | } 32 | 33 | public static ConsoleTableBuilder WithColumn(this ConsoleTableBuilder builder, List columnNames) 34 | { 35 | builder.Column = new List(); 36 | #if NET35 37 | columnNames.ForEach(f => builder.Column.Add(f)); 38 | #else 39 | builder.Column.AddRange(columnNames); 40 | #endif 41 | return builder; 42 | } 43 | 44 | public static ConsoleTableBuilder WithColumn(this ConsoleTableBuilder builder, params string[] columnNames) 45 | { 46 | builder.Column = new List(); 47 | builder.Column.AddRange(new List(columnNames)); 48 | return builder; 49 | } 50 | 51 | public static ConsoleTableBuilder AddRow(this ConsoleTableBuilder builder, params object[] rowValues) 52 | { 53 | if (rowValues == null) 54 | return builder; 55 | 56 | builder.Rows.Add(new List(rowValues)); 57 | 58 | return builder; 59 | } 60 | 61 | public static ConsoleTableBuilder WithMetadataRow(this ConsoleTableBuilder builder, MetaRowPositions position, Func contentGenerator) 62 | { 63 | switch (position) 64 | { 65 | case MetaRowPositions.Top: 66 | if (builder.TopMetadataRows == null) 67 | { 68 | builder.TopMetadataRows = new List>>(); 69 | } 70 | 71 | builder.TopMetadataRows.Add(new KeyValuePair>(position, contentGenerator)); 72 | break; 73 | case MetaRowPositions.Bottom: 74 | if (builder.BottomMetadataRows == null) 75 | { 76 | builder.BottomMetadataRows = new List>>(); 77 | } 78 | 79 | builder.BottomMetadataRows.Add(new KeyValuePair>(position, contentGenerator)); 80 | break; 81 | 82 | default: 83 | break; 84 | } 85 | 86 | return builder; 87 | } 88 | 89 | /// 90 | /// Add title row on top of table 91 | /// 92 | /// 93 | /// 94 | /// 95 | public static ConsoleTableBuilder WithTitle(this ConsoleTableBuilder builder, string title, TextAligntment titleAligntment = TextAligntment.Center) 96 | { 97 | builder.TableTitle = title; 98 | builder.TableTitleTextAlignment = titleAligntment; 99 | return builder; 100 | } 101 | 102 | /// 103 | /// Add title row on top of table 104 | /// 105 | /// 106 | /// 107 | /// text color 108 | /// 109 | public static ConsoleTableBuilder WithTitle(this ConsoleTableBuilder builder, string title, ConsoleColor foregroundColor, TextAligntment titleAligntment = TextAligntment.Center) 110 | { 111 | builder.TableTitle = title; 112 | builder.TableTitleColor = new ConsoleColorNullable(foregroundColor); 113 | builder.TableTitleTextAlignment = titleAligntment; 114 | return builder; 115 | } 116 | 117 | /// 118 | /// Add title row on top of table 119 | /// 120 | /// 121 | /// 122 | /// text color 123 | /// background color 124 | /// 125 | public static ConsoleTableBuilder WithTitle(this ConsoleTableBuilder builder, string title, ConsoleColor foregroundColor, ConsoleColor backgroundColor, TextAligntment titleAligntment = TextAligntment.Center) 126 | { 127 | builder.TableTitle = title; 128 | builder.TableTitleColor = new ConsoleColorNullable(foregroundColor, backgroundColor); 129 | builder.TableTitleTextAlignment = titleAligntment; 130 | return builder; 131 | } 132 | 133 | public static ConsoleTableBuilder WithPaddingLeft(this ConsoleTableBuilder builder, string paddingLeft) 134 | { 135 | builder.PaddingLeft = paddingLeft ?? string.Empty; 136 | return builder; 137 | } 138 | 139 | public static ConsoleTableBuilder WithPaddingRight(this ConsoleTableBuilder builder, string paddingRight) 140 | { 141 | builder.PaddingRight = paddingRight ?? string.Empty; 142 | return builder; 143 | } 144 | 145 | 146 | public static ConsoleTableBuilder WithFormatter(this ConsoleTableBuilder builder, int columnIndex, Func formatter) 147 | { 148 | if (!builder.FormatterStore.ContainsKey(columnIndex)) 149 | { 150 | builder.FormatterStore.Add(columnIndex, formatter); 151 | } 152 | else 153 | { 154 | builder.FormatterStore[columnIndex] = formatter; 155 | } 156 | 157 | return builder; 158 | } 159 | public static ConsoleTableBuilder WithColumnFormatter(this ConsoleTableBuilder builder, int columnIndex, Func formatter) 160 | { 161 | if (!builder.ColumnFormatterStore.ContainsKey(columnIndex)) 162 | { 163 | builder.ColumnFormatterStore.Add(columnIndex, formatter); 164 | } 165 | else 166 | { 167 | builder.ColumnFormatterStore[columnIndex] = formatter; 168 | } 169 | 170 | return builder; 171 | } 172 | 173 | /// 174 | /// Text alignment definition 175 | /// 176 | /// 177 | /// 178 | /// 179 | public static ConsoleTableBuilder WithTextAlignment(this ConsoleTableBuilder builder, Dictionary alignmentData) 180 | { 181 | if (alignmentData != null) 182 | { 183 | builder.TextAligmentData = alignmentData; 184 | } 185 | 186 | return builder; 187 | } 188 | 189 | public static ConsoleTableBuilder WithHeaderTextAlignment(this ConsoleTableBuilder builder, Dictionary alignmentData) 190 | { 191 | if (alignmentData != null) 192 | { 193 | builder.HeaderTextAligmentData = alignmentData; 194 | } 195 | 196 | return builder; 197 | } 198 | 199 | public static ConsoleTableBuilder WithMinLength(this ConsoleTableBuilder builder, Dictionary minLengthData) 200 | { 201 | if (minLengthData != null) 202 | { 203 | builder.MinLengthData = minLengthData; 204 | } 205 | 206 | return builder; 207 | } 208 | 209 | public static ConsoleTableBuilder TrimColumn(this ConsoleTableBuilder builder, bool canTrimColumn = true) 210 | { 211 | builder.CanTrimColumn = canTrimColumn; 212 | return builder; 213 | } 214 | 215 | public static ConsoleTableBuilder AddRow(this ConsoleTableBuilder builder, List row) 216 | { 217 | if (row == null) 218 | return builder; 219 | 220 | builder.Rows.Add(row); 221 | return builder; 222 | } 223 | 224 | public static ConsoleTableBuilder AddRow(this ConsoleTableBuilder builder, List> rows) 225 | { 226 | if (rows == null) 227 | return builder; 228 | 229 | builder.Rows.AddRange(rows); 230 | return builder; 231 | } 232 | 233 | public static ConsoleTableBuilder AddRow(this ConsoleTableBuilder builder, DataRow row) 234 | { 235 | if (row == null) 236 | return builder; 237 | 238 | builder.Rows.Add(new List(row.ItemArray)); 239 | return builder; 240 | } 241 | 242 | public static ConsoleTableBuilder WithFormat(this ConsoleTableBuilder builder, ConsoleTableBuilderFormat format) 243 | { 244 | // reset CharMapPositions 245 | builder.CharMapPositionStore = null; 246 | builder.TableFormat = format; 247 | 248 | switch (builder.TableFormat) 249 | { 250 | case ConsoleTableBuilderFormat.Default: 251 | builder.CharMapPositionStore = new Dictionary 252 | { 253 | { CharMapPositions.TopLeft, '-' }, 254 | { CharMapPositions.TopCenter, '-' }, 255 | { CharMapPositions.TopRight, '-' }, 256 | { CharMapPositions.MiddleLeft, '-' }, 257 | { CharMapPositions.MiddleCenter, '-' }, 258 | { CharMapPositions.MiddleRight, '-' }, 259 | { CharMapPositions.BottomLeft, '-' }, 260 | { CharMapPositions.BottomCenter, '-' }, 261 | { CharMapPositions.BottomRight, '-' }, 262 | { CharMapPositions.BorderTop, '-' }, 263 | { CharMapPositions.BorderLeft, '|' }, 264 | { CharMapPositions.BorderRight, '|' }, 265 | { CharMapPositions.BorderBottom, '-' }, 266 | { CharMapPositions.DividerX, '-' }, 267 | { CharMapPositions.DividerY, '|' }, 268 | }; 269 | break; 270 | case ConsoleTableBuilderFormat.MarkDown: 271 | builder.CharMapPositionStore = new Dictionary 272 | { 273 | { CharMapPositions.DividerY, '|' }, 274 | { CharMapPositions.BorderLeft, '|' }, 275 | { CharMapPositions.BorderRight, '|' }, 276 | }; 277 | 278 | builder.HeaderCharMapPositionStore = new Dictionary 279 | { 280 | { HeaderCharMapPositions.BorderBottom, '-' }, 281 | { HeaderCharMapPositions.BottomLeft, '|' }, 282 | { HeaderCharMapPositions.BottomCenter, '|' }, 283 | { HeaderCharMapPositions.BottomRight, '|' }, 284 | { HeaderCharMapPositions.BorderLeft, '|' }, 285 | { HeaderCharMapPositions.BorderRight, '|' }, 286 | { HeaderCharMapPositions.Divider, '|' }, 287 | }; 288 | break; 289 | case ConsoleTableBuilderFormat.Alternative: 290 | builder.CharMapPositionStore = new Dictionary 291 | { 292 | { CharMapPositions.TopLeft, '+' }, 293 | { CharMapPositions.TopCenter, '+' }, 294 | { CharMapPositions.TopRight, '+' }, 295 | { CharMapPositions.MiddleLeft, '+' }, 296 | { CharMapPositions.MiddleCenter, '+' }, 297 | { CharMapPositions.MiddleRight, '+' }, 298 | { CharMapPositions.BottomLeft, '+' }, 299 | { CharMapPositions.BottomCenter, '+' }, 300 | { CharMapPositions.BottomRight, '+' }, 301 | { CharMapPositions.BorderTop, '-' }, 302 | { CharMapPositions.BorderRight, '|' }, 303 | { CharMapPositions.BorderBottom, '-' }, 304 | { CharMapPositions.BorderLeft, '|' }, 305 | { CharMapPositions.DividerX, '-' }, 306 | { CharMapPositions.DividerY, '|' }, 307 | }; 308 | break; 309 | case ConsoleTableBuilderFormat.Minimal: 310 | builder.CharMapPositionStore = new Dictionary { }; 311 | 312 | builder.HeaderCharMapPositionStore = new Dictionary 313 | { 314 | { HeaderCharMapPositions.BorderBottom, '-' } 315 | }; 316 | 317 | builder.PaddingLeft = string.Empty; 318 | builder.PaddingRight = " "; 319 | break; 320 | default: 321 | break; 322 | } 323 | 324 | return builder; 325 | } 326 | 327 | public static ConsoleTableBuilder WithCharMapDefinition(this ConsoleTableBuilder builder) 328 | { 329 | return builder.WithCharMapDefinition(new Dictionary { }); 330 | } 331 | 332 | public static ConsoleTableBuilder WithCharMapDefinition(this ConsoleTableBuilder builder, Dictionary charMapPositions) 333 | { 334 | builder.CharMapPositionStore = charMapPositions; 335 | return builder; 336 | } 337 | 338 | public static ConsoleTableBuilder WithCharMapDefinition(this ConsoleTableBuilder builder, Dictionary charMapPositions, Dictionary headerCharMapPositions = null) 339 | { 340 | builder.CharMapPositionStore = charMapPositions; 341 | builder.HeaderCharMapPositionStore = headerCharMapPositions; 342 | return builder; 343 | } 344 | 345 | public static ConsoleTableBuilder WithHeaderCharMapDefinition(this ConsoleTableBuilder builder, Dictionary headerCharMapPositions = null) 346 | { 347 | builder.HeaderCharMapPositionStore = headerCharMapPositions; 348 | return builder; 349 | } 350 | 351 | public static StringBuilder Export(this ConsoleTableBuilder builder) 352 | { 353 | var numberOfColumns = 0; 354 | if (builder.Rows.Any()) 355 | { 356 | numberOfColumns = builder.Rows.Max(x => x.Count); 357 | } 358 | else 359 | { 360 | if (builder.Column != null) 361 | { 362 | numberOfColumns = builder.Column.Count(); 363 | } 364 | } 365 | 366 | if (numberOfColumns == 0) 367 | { 368 | return new StringBuilder(); 369 | } 370 | 371 | if (builder.Column == null) 372 | { 373 | numberOfColumns = 0; 374 | } 375 | else 376 | { 377 | if (numberOfColumns < builder.Column.Count) 378 | { 379 | numberOfColumns = builder.Column.Count; 380 | } 381 | } 382 | 383 | for (int i = 0; i < 1; i++) 384 | { 385 | if (builder.Column != null && builder.Column.Count < numberOfColumns) 386 | { 387 | var missCount = numberOfColumns - builder.Column.Count; 388 | for (int j = 0; j < missCount; j++) 389 | { 390 | builder.Column.Add(null); 391 | } 392 | } 393 | } 394 | 395 | for (int i = 0; i < builder.Rows.Count; i++) 396 | { 397 | if (builder.Rows[i].Count < numberOfColumns) 398 | { 399 | var missCount = numberOfColumns - builder.Rows[i].Count; 400 | for (int j = 0; j < missCount; j++) 401 | { 402 | builder.Rows[i].Add(null); 403 | } 404 | } 405 | } 406 | 407 | return CreateTableForCustomFormat(builder); 408 | } 409 | 410 | public static void ExportAndWrite(this ConsoleTableBuilder builder, TableAligntment alignment = TableAligntment.Left) 411 | { 412 | var strBuilder = builder.Export(); 413 | var lines = strBuilder.ToString().Split('\n'); 414 | 415 | var linesCount = lines.Count(); 416 | for (int i = 0; i < linesCount; i++) 417 | { 418 | var row = string.Empty; 419 | 420 | switch (alignment) 421 | { 422 | case TableAligntment.Left: 423 | row = lines[i]; 424 | break; 425 | case TableAligntment.Center: 426 | row = String.Format("{0," + ((Console.WindowWidth / 2) + (lines[i].RealLength(true) / 2) - (lines[i].RealLength(true) - lines[i].Length)) + "}", lines[i]); 427 | break; 428 | case TableAligntment.Right: 429 | var count = Console.WindowWidth - lines[i].RealLength(true); 430 | row = new string(' ', count<0?0: count) + lines[i]; 431 | break; 432 | } 433 | 434 | if (i == 0 435 | && !string.IsNullOrEmpty(builder.TableTitle) 436 | && builder.TableTitle.Trim().Length != 0 437 | && !builder.TableTitleColor.IsForegroundColorNull 438 | && builder.TitlePositionStartAt > 0 439 | && builder.TitlePositionLength > 0) 440 | { 441 | var newTitlePositionStartAt = builder.TitlePositionStartAt + (row.Length - lines[i].Length); 442 | 443 | Console.Write(row.Substring(0, newTitlePositionStartAt)); 444 | Console.ForegroundColor = builder.TableTitleColor.ForegroundColor; 445 | if (!builder.TableTitleColor.IsBackgroundColorNull) 446 | { 447 | Console.BackgroundColor = builder.TableTitleColor.BackgroundColor; 448 | } 449 | Console.Write(row.Substring(newTitlePositionStartAt, builder.TitlePositionLength)); 450 | Console.ResetColor(); 451 | Console.Write(row.Substring(newTitlePositionStartAt + builder.TitlePositionLength, row.Length - (newTitlePositionStartAt + builder.TitlePositionLength))); 452 | Console.Write('\n'); 453 | } 454 | else 455 | { 456 | if (i == linesCount - 2) 457 | { 458 | if (row.EndsWith('\r'.ToString())) 459 | { 460 | Console.Write(row.Substring(0, row.Length - 1)); 461 | } 462 | else 463 | { 464 | Console.Write(row); 465 | } 466 | } 467 | else 468 | { 469 | if (i == linesCount - 1) // is last line 470 | { 471 | Console.Write(row); 472 | } 473 | else 474 | { 475 | Console.WriteLine(row); 476 | } 477 | } 478 | } 479 | } 480 | } 481 | 482 | public static void ExportAndWriteLine(this ConsoleTableBuilder builder, TableAligntment alignment = TableAligntment.Left) 483 | { 484 | builder.ExportAndWrite(alignment); 485 | Console.Write('\n'); 486 | } 487 | 488 | private static StringBuilder CreateTableForCustomFormat(ConsoleTableBuilder builder) 489 | { 490 | if (builder.CharMapPositionStore == null) 491 | { 492 | builder.WithFormat(ConsoleTableBuilderFormat.Default); 493 | } 494 | 495 | builder.PopulateFormattedColumnsRows(); 496 | var columnLengths = builder.GetCadidateColumnLengths(); 497 | var columnNoUtf8CharasLengths = builder.GetCadidateColumnLengths(false); 498 | builder.CenterRowContent(columnLengths); 499 | 500 | var filledMap = FillCharMap(builder.CharMapPositionStore); 501 | var filledHeaderMap = FillHeaderCharMap(builder.HeaderCharMapPositionStore); 502 | 503 | var strBuilder = new StringBuilder(); 504 | var topMetadataStringBuilder = BuildMetaRowsFormat(builder, MetaRowPositions.Top); 505 | for (int i = 0; i < topMetadataStringBuilder.Count; i++) 506 | { 507 | strBuilder.AppendLine(topMetadataStringBuilder[i]); 508 | } 509 | 510 | var tableTopLine = builder.CreateTableTopLine(columnLengths, filledMap); 511 | var tableRowContentFormat = builder.CreateTableContentLineFormat(columnLengths, filledMap); 512 | var tableMiddleLine = builder.CreateTableMiddleLine(columnLengths, filledMap); 513 | var tableBottomLine = builder.CreateTableBottomLine(columnLengths, filledMap); 514 | 515 | var headerTopLine = string.Empty; 516 | var headerRowContentFormat = string.Empty; 517 | var headerBottomLine = string.Empty; 518 | 519 | if (filledHeaderMap != null) 520 | { 521 | headerTopLine = builder.CreateHeaderTopLine(columnLengths, filledMap, filledHeaderMap); 522 | headerRowContentFormat = builder.CreateHeaderContentLineFormat(columnLengths, filledMap, filledHeaderMap); 523 | headerBottomLine = builder.CreateHeaderBottomLine(columnLengths, filledMap, filledHeaderMap); 524 | } 525 | 526 | // find the longest formatted line 527 | //var maxRowLength = Math.Max(0, builder.Rows.Any() ? builder.Rows.Max(row => string.Format(tableRowContentFormat, row.ToArray()).Length) : 0); 528 | 529 | var hasHeader = builder.FormattedColumns != null && builder.FormattedColumns.Any() && builder.FormattedColumns.Max(x => (x ?? string.Empty).ToString().Length) > 0; 530 | 531 | // header 532 | if (hasHeader) 533 | { 534 | if (headerTopLine != null && headerTopLine.Trim().Length > 0) 535 | { 536 | strBuilder.AppendLine(headerTopLine); 537 | } 538 | else 539 | { 540 | if (tableTopLine != null && tableTopLine.Trim().Length > 0) 541 | { 542 | strBuilder.AppendLine(tableTopLine); 543 | } 544 | } 545 | 546 | var headerSlices = builder.FormattedColumns.ToArray(); 547 | var formattedHeaderSlice = builder.CenterColumnContent(headerSlices, columnLengths); 548 | 549 | //var formattedHeaderSlice = Enumerable.Range(0, headerSlices.Length).Select(idx => builder.ColumnFormatterStore.ContainsKey(idx) ? builder.ColumnFormatterStore[idx](headerSlices[idx] == null ? string.Empty : headerSlices[idx].ToString()) : headerSlices[idx] == null ? string.Empty : headerSlices[idx].ToString()).ToArray(); 550 | //formattedHeaderSlice = builder.CenterColumnContent(headerSlices, columnLengths); 551 | 552 | if (headerRowContentFormat != null && headerRowContentFormat.Trim().Length > 0) 553 | { 554 | strBuilder.AppendLine(string.Format(headerRowContentFormat, formattedHeaderSlice)); 555 | } 556 | else 557 | { 558 | strBuilder.AppendLine(string.Format(tableRowContentFormat, formattedHeaderSlice)); 559 | } 560 | } 561 | //else 562 | //{ 563 | // if (beginTableFormat.Length > 0) strBuilder.AppendLine(beginTableFormat); 564 | // strBuilder.AppendLine(string.Format(rowContentTableFormat, builder.FormattedColumns.ToArray())); 565 | //} 566 | 567 | // add each row 568 | 569 | //var results = builder.Rows.Select(row => { 570 | // var rowSlices = row.ToArray(); 571 | // return string.Format(tableRowContentFormat, Enumerable.Range(0, rowSlices.Length).Select(idx => builder.FormatterStore.ContainsKey(idx) ? builder.FormatterStore[idx](rowSlices[idx] == null ? string.Empty : rowSlices[idx].ToString()) : rowSlices[idx] == null ? string.Empty : rowSlices[idx].ToString()).ToArray()); 572 | //}).ToList(); 573 | 574 | var results = builder.FormattedRows.Select(row => 575 | { 576 | var rowFormate = builder.CreateRawLineFormat(columnLengths, filledMap, row.ToArray()); 577 | return string.Format(rowFormate, row.ToArray()); 578 | 579 | }).ToList(); 580 | 581 | var isFirstRow = true; 582 | foreach (var row in results) 583 | { 584 | if (isFirstRow) 585 | { 586 | if (hasHeader) 587 | { 588 | if ((string.IsNullOrEmpty(headerBottomLine) || headerBottomLine.Length == 0) && tableMiddleLine.Length > 0) 589 | { 590 | strBuilder.AppendLine(tableMiddleLine); 591 | } 592 | else 593 | { 594 | if (headerBottomLine.Length > 0) 595 | { 596 | strBuilder.AppendLine(headerBottomLine); 597 | } 598 | } 599 | } 600 | else 601 | { 602 | if (tableTopLine.Length > 0) 603 | { 604 | strBuilder.AppendLine(tableTopLine); 605 | } 606 | } 607 | 608 | isFirstRow = false; 609 | } 610 | else 611 | { 612 | if (tableMiddleLine.Length > 0) 613 | { 614 | strBuilder.AppendLine(tableMiddleLine); 615 | } 616 | } 617 | 618 | strBuilder.AppendLine(row); 619 | } 620 | 621 | if (results.Any()) 622 | { 623 | if (tableBottomLine.Length > 0) 624 | { 625 | strBuilder.AppendLine(tableBottomLine); 626 | } 627 | } 628 | else 629 | { 630 | if ((string.IsNullOrEmpty(headerBottomLine) || headerBottomLine.Length == 0) && tableBottomLine.Length > 0) 631 | { 632 | strBuilder.AppendLine(tableBottomLine); 633 | } 634 | else 635 | { 636 | if (headerBottomLine.Length > 0) 637 | { 638 | strBuilder.AppendLine(headerBottomLine); 639 | } 640 | } 641 | } 642 | 643 | var bottomMetadataStringBuilder = BuildMetaRowsFormat(builder, MetaRowPositions.Bottom); 644 | for (int i = 0; i < bottomMetadataStringBuilder.Count; i++) 645 | { 646 | strBuilder.AppendLine(bottomMetadataStringBuilder[i]); 647 | } 648 | 649 | return strBuilder; 650 | } 651 | 652 | //private static StringBuilder CreateTableForDefaultFormat(ConsoleTableBuilder builder) 653 | //{ 654 | // var strBuilder = new StringBuilder(); 655 | // BuildMetaRowsFormat(builder, strBuilder, MetaRowPositions.Top); 656 | 657 | // // create the string format with padding 658 | // var format = builder.Format('|'); 659 | 660 | // if (format == string.Empty) 661 | // { 662 | // return strBuilder; 663 | // } 664 | 665 | // // find the longest formatted line 666 | // var maxRowLength = Math.Max(0, builder.Rows.Any() ? builder.Rows.Max(row => string.Format(format, row.ToArray()).Length) : 0); 667 | 668 | // // add each row 669 | // var results = builder.Rows.Select(row => string.Format(format, row.ToArray())).ToList(); 670 | 671 | // // create the divider 672 | // var divider = new string('-', maxRowLength); 673 | 674 | // // header 675 | // if (builder.Column != null && builder.Column.Any() && builder.Column.Max(x => (x ?? string.Empty).ToString().Length) > 0) 676 | // { 677 | // strBuilder.AppendLine(divider); 678 | // strBuilder.AppendLine(string.Format(format, builder.Column.ToArray())); 679 | // } 680 | 681 | // foreach (var row in results) 682 | // { 683 | // strBuilder.AppendLine(divider); 684 | // strBuilder.AppendLine(row); 685 | // } 686 | 687 | // strBuilder.AppendLine(divider); 688 | 689 | // BuildMetaRowsFormat(builder, strBuilder, MetaRowPositions.Bottom); 690 | // return strBuilder; 691 | //} 692 | 693 | //private static StringBuilder CreateTableForMinimalFormat(ConsoleTableBuilder builder) 694 | //{ 695 | // var strBuilder = new StringBuilder(); 696 | // BuildMetaRowsFormat(builder, strBuilder, MetaRowPositions.Top); 697 | 698 | // // create the string format with padding 699 | // var format = builder.Format('\0').Trim(); 700 | 701 | // if (format == string.Empty) 702 | // { 703 | // return strBuilder; 704 | // } 705 | 706 | // var skipFirstRow = false; 707 | // var columnHeaders = string.Empty; 708 | 709 | // if (builder.Column != null && builder.Column.Any() && builder.Column.Max(x => (x ?? string.Empty).ToString().Length) > 0) 710 | // { 711 | // skipFirstRow = false; 712 | // columnHeaders = string.Format(format, builder.Column.ToArray()); 713 | // } 714 | // else 715 | // { 716 | // skipFirstRow = true; 717 | // columnHeaders = string.Format(format, builder.Rows.First().ToArray()); 718 | // } 719 | 720 | // // create the divider 721 | // var divider = Regex.Replace(columnHeaders, @"[^|]", '-'.ToString()); 722 | 723 | // strBuilder.AppendLine(columnHeaders); 724 | // strBuilder.AppendLine(divider); 725 | 726 | // // add each row 727 | // var results = builder.Rows.Skip(skipFirstRow ? 1 : 0).Select(row => string.Format(format, row.ToArray())).ToList(); 728 | // results.ForEach(row => strBuilder.AppendLine(row)); 729 | 730 | // BuildMetaRowsFormat(builder, strBuilder, MetaRowPositions.Bottom); 731 | 732 | // return strBuilder; 733 | //} 734 | 735 | //private static StringBuilder CreateTableForMarkdownFormat(ConsoleTableBuilder builder) 736 | //{ 737 | // var strBuilder = new StringBuilder(); 738 | // BuildMetaRowsFormat(builder, strBuilder, MetaRowPositions.Top); 739 | 740 | // // create the string format with padding 741 | // var format = builder.Format('|'); 742 | 743 | // if (format == string.Empty) 744 | // { 745 | // return strBuilder; 746 | // } 747 | 748 | // var skipFirstRow = false; 749 | // var columnHeaders = string.Empty; 750 | 751 | // if (builder.Column != null && builder.Column.Any() && builder.Column.Max(x => (x ?? string.Empty).ToString().Length) > 0) 752 | // { 753 | // skipFirstRow = false; 754 | // columnHeaders = string.Format(format, builder.Column.ToArray()); 755 | // } 756 | // else 757 | // { 758 | // skipFirstRow = true; 759 | // columnHeaders = string.Format(format, builder.Rows.First().ToArray()); 760 | // } 761 | 762 | // // create the divider 763 | // var divider = Regex.Replace(columnHeaders, @"[^|]", '-'.ToString()); 764 | 765 | // strBuilder.AppendLine(columnHeaders); 766 | // strBuilder.AppendLine(divider); 767 | 768 | // // add each row 769 | // var results = builder.Rows.Skip(skipFirstRow ? 1 : 0).Select(row => string.Format(format, row.ToArray())).ToList(); 770 | // results.ForEach(row => strBuilder.AppendLine(row)); 771 | 772 | // BuildMetaRowsFormat(builder, strBuilder, MetaRowPositions.Bottom); 773 | 774 | // return strBuilder; 775 | //} 776 | 777 | //private static StringBuilder CreateTableForAlternativeFormat(ConsoleTableBuilder builder) 778 | //{ 779 | // var strBuilder = new StringBuilder(); 780 | // BuildMetaRowsFormat(builder, strBuilder, MetaRowPositions.Top); 781 | 782 | // // create the string format with padding 783 | // var format = builder.Format('|'); 784 | 785 | // if (format == string.Empty) 786 | // { 787 | // return strBuilder; 788 | // } 789 | 790 | // var skipFirstRow = false; 791 | // var columnHeaders = string.Empty; 792 | 793 | // if (builder.Column != null && builder.Column.Any() && builder.Column.Max(x => (x ?? string.Empty).ToString().Length) > 0) 794 | // { 795 | // skipFirstRow = false; 796 | // columnHeaders = string.Format(format, builder.Column.ToArray()); 797 | // } 798 | // else 799 | // { 800 | // skipFirstRow = true; 801 | // columnHeaders = string.Format(format, builder.Rows.First().ToArray()); 802 | // } 803 | 804 | // // create the divider 805 | // var divider = Regex.Replace(columnHeaders, @"[^|]", '-'.ToString()); 806 | // var dividerPlus = divider.Replace("|", "+"); 807 | 808 | // strBuilder.AppendLine(dividerPlus); 809 | // strBuilder.AppendLine(columnHeaders); 810 | 811 | // // add each row 812 | // var results = builder.Rows.Skip(skipFirstRow ? 1 : 0).Select(row => string.Format(format, row.ToArray())).ToList(); 813 | 814 | // foreach (var row in results) 815 | // { 816 | // strBuilder.AppendLine(dividerPlus); 817 | // strBuilder.AppendLine(row); 818 | // } 819 | // strBuilder.AppendLine(dividerPlus); 820 | 821 | // BuildMetaRowsFormat(builder, strBuilder, MetaRowPositions.Bottom); 822 | // return strBuilder; 823 | //} 824 | 825 | private static List BuildMetaRowsFormat(ConsoleTableBuilder builder, MetaRowPositions position) 826 | { 827 | var result = new List(); 828 | switch (position) 829 | { 830 | case MetaRowPositions.Top: 831 | if (builder.TopMetadataRows.Any()) 832 | { 833 | foreach (var item in builder.TopMetadataRows) 834 | { 835 | if (item.Value != null) 836 | { 837 | result.Add(item.Value.Invoke(builder)); 838 | } 839 | } 840 | } 841 | break; 842 | case MetaRowPositions.Bottom: 843 | if (builder.BottomMetadataRows.Any()) 844 | { 845 | foreach (var item in builder.BottomMetadataRows) 846 | { 847 | if (item.Value != null) 848 | { 849 | result.Add(item.Value.Invoke(builder)); 850 | } 851 | } 852 | } 853 | break; 854 | default: 855 | break; 856 | } 857 | 858 | return result; 859 | } 860 | 861 | private static Dictionary FillCharMap(Dictionary definition) 862 | { 863 | if (definition == null) 864 | { 865 | return new Dictionary(); 866 | } 867 | 868 | var filledMap = definition; 869 | 870 | foreach (CharMapPositions c in (CharMapPositions[])Enum.GetValues(typeof(CharMapPositions))) 871 | { 872 | if (!filledMap.ContainsKey(c)) 873 | { 874 | filledMap.Add(c, '\0'); 875 | } 876 | } 877 | 878 | return filledMap; 879 | } 880 | 881 | private static Dictionary FillHeaderCharMap(Dictionary definition) 882 | { 883 | if (definition == null) 884 | { 885 | return null; 886 | } 887 | 888 | var filledMap = definition; 889 | 890 | foreach (HeaderCharMapPositions c in (HeaderCharMapPositions[])Enum.GetValues(typeof(HeaderCharMapPositions))) 891 | { 892 | if (!filledMap.ContainsKey(c)) 893 | { 894 | filledMap.Add(c, '\0'); 895 | } 896 | } 897 | 898 | return filledMap; 899 | } 900 | 901 | } 902 | } 903 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/ConsoleTableExt.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0;net46;net35 4 | ConsoleTableExt 5 | 3.3.0 6 | Copyright 2017 minhhungit 7 | A fluent library to print out a nicely formatted table in a console application C# 8 | Hung Vo 9 | https://github.com/minhhungit/ConsoleTableExt/blob/master/LICENSE 10 | https://github.com/minhhungit/ConsoleTableExt/ 11 | https://github.com/minhhungit/ConsoleTableExt/ 12 | console text-table fluent console-table6 13 | workarround issue 42 https://github.com/minhhungit/ConsoleTableExt/issues/42 14 | "Parameter count mismatch" exception when passing lists of strings 15 | 3.1.9 16 | 3.1.9 17 | 18 | https://raw.githubusercontent.com/minhhungit/ConsoleTableExt/master/wiki/Images/icon.ico 19 | 20 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/Enums/CharMapPositions.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace ConsoleTableExt 3 | { 4 | /// 5 | /// Check map here https://raw.githubusercontent.com/minhhungit/ConsoleTableExt/master/wiki/Images/CharMapPositions.png 6 | /// 7 | public enum CharMapPositions 8 | { 9 | TopLeft, 10 | TopCenter, 11 | TopRight, 12 | MiddleLeft, 13 | MiddleCenter, 14 | MiddleRight, 15 | BottomLeft, 16 | BottomCenter, 17 | BottomRight, 18 | BorderTop, 19 | BorderRight, 20 | BorderBottom, 21 | BorderLeft, 22 | DividerX, 23 | DividerY 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/Enums/ConsoleTableBuilderFormat.cs: -------------------------------------------------------------------------------- 1 | namespace ConsoleTableExt 2 | { 3 | public enum ConsoleTableBuilderFormat 4 | { 5 | Default = 0, 6 | MarkDown = 1, 7 | Alternative = 2, 8 | Minimal = 3 9 | } 10 | } -------------------------------------------------------------------------------- /Src/ConsoleTableExt/Enums/HeaderCharMapPositions.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace ConsoleTableExt 3 | { 4 | /// 5 | /// Check map here https://raw.githubusercontent.com/minhhungit/ConsoleTableExt/master/wiki/Images/HeaderCharMapPositions.png 6 | /// 7 | public enum HeaderCharMapPositions 8 | { 9 | TopLeft, 10 | TopCenter, 11 | TopRight, 12 | BottomLeft, 13 | BottomCenter, 14 | BottomRight, 15 | BorderTop, 16 | BorderRight, 17 | BorderBottom, 18 | BorderLeft, 19 | Divider, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/Enums/MetaRowPositions.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace ConsoleTableExt 3 | { 4 | public enum MetaRowPositions 5 | { 6 | Top = 0, 7 | Bottom = 1 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/Enums/TableAligntment.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace ConsoleTableExt 3 | { 4 | public enum TableAligntment 5 | { 6 | Left = 0, 7 | Right = 1, 8 | Center = 2 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/Enums/TextAligntment.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace ConsoleTableExt 3 | { 4 | public enum TextAligntment 5 | { 6 | Left = 0, 7 | Right = 1, 8 | Center = 2 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Src/ConsoleTableExt/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace ConsoleTableExt 6 | { 7 | public static class StringExtensions 8 | { 9 | public static int RealLength(this string value,bool withUtf8Characters) 10 | { 11 | if (string.IsNullOrEmpty(value)) 12 | return 0; 13 | 14 | if (!withUtf8Characters) 15 | return value.Length; 16 | 17 | int i = 0;//count 18 | foreach (char newChar in value) 19 | { 20 | if (newChar >= 0x4e00 && newChar <= 0x9fbb) 21 | { 22 | //utf-8 characters 23 | i += 2; 24 | } 25 | else 26 | { 27 | i++; 28 | } 29 | } 30 | return i; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test-regex.txt: -------------------------------------------------------------------------------- 1 | static void Main(string[] args) 2 | { 3 | var format = "{0,15} │ {1,30} │ {2,*13} │ {3,*2} │ {4,-14}"; 4 | 5 | var items = Regex.Replace(format, @"\{(.*?)}", m => 6 | { 7 | if (m.Value.Contains("*")) 8 | { 9 | var newValue = m.Value.Replace("*", ""); 10 | int oldLength = int.Parse(newValue.Replace("{", "").Replace("}", "").Split(',')[1]); 11 | 12 | return string.Join("", Enumerable.Range(0, oldLength / 2).Select(x => " ")) + m.Value.Replace(string.Format(",*{0}", oldLength), string.Format(",{0}", oldLength / 2)); 13 | } 14 | return m.Value; 15 | }); 16 | 17 | foreach (var item in items) 18 | { 19 | Console.WriteLine(item); 20 | } 21 | Console.WriteLine("Hello World!"); 22 | } 23 | 24 | // https://www.jerriepelser.com/blog/using-ansi-color-codes-in-net-console-apps/ 25 | // https://en.wikipedia.org/wiki/ANSI_escape_code -------------------------------------------------------------------------------- /wiki/Images/CharMapPositions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhhungit/ConsoleTableExt/107f5d4dd0f2de968d955deeea5dfb771c4b7e98/wiki/Images/CharMapPositions.png -------------------------------------------------------------------------------- /wiki/Images/HeaderCharMapPositions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhhungit/ConsoleTableExt/107f5d4dd0f2de968d955deeea5dfb771c4b7e98/wiki/Images/HeaderCharMapPositions.png -------------------------------------------------------------------------------- /wiki/Images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhhungit/ConsoleTableExt/107f5d4dd0f2de968d955deeea5dfb771c4b7e98/wiki/Images/demo.png -------------------------------------------------------------------------------- /wiki/Images/demo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhhungit/ConsoleTableExt/107f5d4dd0f2de968d955deeea5dfb771c4b7e98/wiki/Images/demo1.png -------------------------------------------------------------------------------- /wiki/Images/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minhhungit/ConsoleTableExt/107f5d4dd0f2de968d955deeea5dfb771c4b7e98/wiki/Images/icon.ico --------------------------------------------------------------------------------