diff --git a/src/MiniExcel/OpenXml/Constants/WorksheetXml.cs b/src/MiniExcel/OpenXml/Constants/WorksheetXml.cs
index 5f1f7202..6b625222 100644
--- a/src/MiniExcel/OpenXml/Constants/WorksheetXml.cs
+++ b/src/MiniExcel/OpenXml/Constants/WorksheetXml.cs
@@ -13,9 +13,34 @@ internal class WorksheetXml
internal static string Dimension(string dimensionRef)
=> $"{StartDimension}{dimensionRef}\"/>";
+ internal const string StartSheetViews = "";
+ internal const string EndSheetViews = "";
+
+ internal static string StartSheetView( int tabSelected=1, int workbookViewId=0 )
+ => $"";
+ internal const string EndSheetView = "";
+
internal const string StartSheetData = "";
internal const string EndSheetData = "";
+ internal static string StartPane( int? xSplit, int? ySplit, string topLeftCell, string activePane, string state )
+ => string.Concat(
+ "");
+
+ internal static string PaneSelection( string pane, string activeCell, string sqref)
+ => string.Concat(
+ $"");
+
internal static string StartRow(int rowIndex)
=> $"";
internal const string EndRow = "";
diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs
index 79e211d6..35d1e430 100644
--- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs
+++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs
@@ -1,5 +1,4 @@
-using MiniExcelLibs.Attributes;
-using MiniExcelLibs.OpenXml.Constants;
+using MiniExcelLibs.OpenXml.Constants;
using MiniExcelLibs.OpenXml.Models;
using MiniExcelLibs.Utils;
using MiniExcelLibs.Zip;
@@ -7,12 +6,10 @@
using System.Collections;
using System.Collections.Generic;
using System.Data;
-using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
-using static MiniExcelLibs.Utils.ImageHelper;
namespace MiniExcelLibs.OpenXml
{
@@ -136,6 +133,9 @@ private void GenerateSheetByIDataReader(MiniExcelStreamWriter writer, IDataReade
}
maxColumnIndex = props.Count;
+ //sheet view
+ WriteSheetViews(writer);
+
WriteColumnsWidths(writer, props);
writer.Write(WorksheetXml.StartSheetData);
@@ -260,6 +260,9 @@ private void GenerateSheetByEnumerable(MiniExcelStreamWriter writer, IEnumerable
writer.Write(WorksheetXml.Dimension(GetDimensionRef(maxRowIndex, maxColumnIndex)));
}
+ //sheet view
+ WriteSheetViews(writer);
+
//cols:width
WriteColumnsWidths(writer, props);
@@ -331,6 +334,9 @@ private void GenerateSheetByDataTable(MiniExcelStreamWriter writer, DataTable va
var prop = GetColumnInfosFromDynamicConfiguration(columnName);
props.Add(prop);
}
+
+ //sheet view
+ WriteSheetViews(writer);
WriteColumnsWidths(writer, props);
@@ -389,6 +395,91 @@ private static void WriteColumnsWidths(MiniExcelStreamWriter writer, IEnumerable
writer.Write(WorksheetXml.EndCols);
}
+ private void WriteSheetViews(MiniExcelStreamWriter writer) {
+ // exit early if no style to write
+ if (_configuration.FreezeRowCount <= 0 && _configuration.FreezeColumnCount <= 0)
+ {
+ return;
+ }
+
+ // start sheetViews
+ writer.Write(WorksheetXml.StartSheetViews);
+ writer.Write(WorksheetXml.StartSheetView());
+
+ // Write panes
+ WritePanes(writer);
+
+ // end sheetViews
+ writer.Write(WorksheetXml.EndSheetView);
+ writer.Write(WorksheetXml.EndSheetViews);
+ }
+
+ private void WritePanes(MiniExcelStreamWriter writer) {
+
+ string activePane;
+ if (_configuration.FreezeColumnCount > 0 && _configuration.FreezeRowCount > 0)
+ {
+ activePane = "bottomRight";
+ }
+ else if (_configuration.FreezeColumnCount > 0)
+ {
+ activePane = "topRight";
+ }
+ else
+ {
+ activePane = "bottomLeft";
+ }
+ writer.Write( WorksheetXml.StartPane(
+ xSplit: _configuration.FreezeColumnCount > 0 ? _configuration.FreezeColumnCount : (int?)null,
+ ySplit: _configuration.FreezeRowCount > 0 ? _configuration.FreezeRowCount : (int?)null,
+ topLeftCell: ExcelOpenXmlUtils.ConvertXyToCell(
+ _configuration.FreezeColumnCount + 1,
+ _configuration.FreezeRowCount + 1
+ ),
+ activePane: activePane,
+ state: "frozen"
+ ) );
+
+ // write pane selections
+ if (_configuration.FreezeColumnCount > 0 && _configuration.FreezeRowCount > 0)
+ {
+ // freeze row and column
+ /*
+
+
+
+ */
+ var cellTR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount+1, 1);
+ writer.Write(WorksheetXml.PaneSelection("topRight", cellTR, cellTR));
+
+ var cellBL = ExcelOpenXmlUtils.ConvertXyToCell(1, _configuration.FreezeRowCount+1);
+ writer.Write(WorksheetXml.PaneSelection("bottomLeft", cellBL, cellBL));
+
+ var cellBR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount+1, _configuration.FreezeRowCount+1);
+ writer.Write(WorksheetXml.PaneSelection("bottomRight", cellBR, cellBR));
+ }
+ else if ( _configuration.FreezeColumnCount > 0 )
+ {
+ // freeze column
+ /*
+
+ */
+ var cellTR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount, 1);
+ writer.Write(WorksheetXml.PaneSelection("topRight", cellTR, cellTR));
+
+ }
+ else
+ {
+ // freeze row
+ /*
+
+ */
+ writer.Write(WorksheetXml.PaneSelection("bottomLeft", null, null));
+
+ }
+
+ }
+
private static void PrintHeader(MiniExcelStreamWriter writer, List props)
{
var xIndex = 1;
diff --git a/src/MiniExcel/OpenXml/OpenXmlConfiguration.cs b/src/MiniExcel/OpenXml/OpenXmlConfiguration.cs
index d4d320ac..218d9f42 100644
--- a/src/MiniExcel/OpenXml/OpenXmlConfiguration.cs
+++ b/src/MiniExcel/OpenXml/OpenXmlConfiguration.cs
@@ -8,6 +8,8 @@ public class OpenXmlConfiguration : Configuration
public bool FillMergedCells { get; set; }
public TableStyles TableStyles { get; set; } = TableStyles.Default;
public bool AutoFilter { get; set; } = true;
+ public int FreezeRowCount { get; set; } = 1;
+ public int FreezeColumnCount { get; set; } = 0;
public bool EnableConvertByteArray { get; set; } = true;
public bool IgnoreTemplateParameterMissing { get; set; } = true;
public bool EnableWriteNullValueCell { get; set; } = true;
diff --git a/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs b/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs
index 5af73bb3..5155b3cc 100644
--- a/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs
+++ b/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs
@@ -56,10 +56,10 @@ public void SaveAsControlChracter()
string path = GetTempXlsxPath();
char[] chars = new char[] {'\u0000','\u0001','\u0002','\u0003','\u0004','\u0005','\u0006','\u0007','\u0008',
'\u0009', //
- '\u000A', //
- '\u000B','\u000C',
+ '\u000A', //
+ '\u000B','\u000C',
'\u000D', //
- '\u000E','\u000F','\u0010','\u0011','\u0012','\u0013','\u0014','\u0015','\u0016',
+ '\u000E','\u000F','\u0010','\u0011','\u0012','\u0013','\u0014','\u0015','\u0016',
'\u0017','\u0018','\u0019','\u001A','\u001B','\u001C','\u001D','\u001E','\u001F','\u007F'
};
var input = chars.Select(s => new { Test = s.ToString() });
@@ -823,6 +823,67 @@ public void SaveAsByIEnumerableIDictionary()
}
}
+ [Fact()]
+ public void SaveAsFrozenRowsAndColumnsTest() {
+
+ var config = new OpenXmlConfiguration
+ {
+ FreezeRowCount = 1,
+ FreezeColumnCount = 2
+ };
+
+ {
+ // Test enumerable
+ var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
+ MiniExcel.SaveAs(
+ path,
+ new[] {
+ new { Column1 = "MiniExcel", Column2 = 1 },
+ new { Column1 = "Github", Column2 = 2}
+ },
+ configuration: config
+ );
+
+ using (var stream = File.OpenRead(path)) {
+ var rows = stream.Query(useHeaderRow: true).ToList();
+
+ Assert.Equal("MiniExcel", rows[0].Column1);
+ Assert.Equal(1, rows[0].Column2);
+ Assert.Equal("Github", rows[1].Column1);
+ Assert.Equal(2, rows[1].Column2);
+ }
+
+ Assert.Equal("A1:B3", Helpers.GetFirstSheetDimensionRefValue(path));
+ //File.Delete(path);
+ }
+
+ {
+ // test table
+ var table = new DataTable();
+ {
+ table.Columns.Add("a", typeof(string));
+ table.Columns.Add("b", typeof(decimal));
+ table.Columns.Add("c", typeof(bool));
+ table.Columns.Add("d", typeof(DateTime));
+ table.Rows.Add("some text", 1234567890, true, DateTime.Now);
+ table.Rows.Add(@"Hello World", -1234567890, false, DateTime.Now.Date);
+ }
+ var pathTable = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
+ MiniExcel.SaveAs(pathTable, table, configuration: config );
+
+ Assert.Equal("A1:D3", Helpers.GetFirstSheetDimensionRefValue(pathTable));
+
+
+ // data reader
+ var reader = table.CreateDataReader();
+ var pathReader = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
+
+ MiniExcel.SaveAs(pathReader, reader, configuration: config);
+ Assert.Equal("A1:D3", Helpers.GetFirstSheetDimensionRefValue(pathTable));
+ }
+
+ }
+
[Fact()]
public void SaveAsByDapperRows()
{