@@ -30,6 +30,7 @@ public class XRowInfo
3030 public IEnumerable CellIEnumerableValues { get ; set ; }
3131 public XMergeCell IEnumerableMercell { get ; set ; }
3232 public List < XMergeCell > RowMercells { get ; set ; }
33+ public List < XmlElement > ConditionalFormats { get ; set ; }
3334 }
3435
3536 public class PropInfo
@@ -153,6 +154,64 @@ private void GetMercells(XmlDocument doc, XmlNode worksheet)
153154 }
154155 }
155156
157+ private static readonly Regex _cellRegex = new Regex ( "([A-Z]+)([0-9]+)" ) ;
158+ private static IEnumerable < ConditionalFormatRange > ParseConditionalFormatRanges ( XmlDocument doc )
159+ {
160+ var conditionalFormatting = doc . SelectNodes ( "/x:worksheet/x:conditionalFormatting" , _ns ) ;
161+
162+ for ( var i = 0 ; i < conditionalFormatting . Count ; ++ i )
163+ {
164+ var conditionalFormat = conditionalFormatting [ i ] ;
165+ var rangeValue = conditionalFormat . Attributes [ "sqref" ] ? . Value ;
166+ var rangeValues = rangeValue ? . Split ( ' ' ) ;
167+ var rangeList = new List < Range > ( ) ;
168+ foreach ( var rangeVal in rangeValues )
169+ {
170+ var rangeValSplit = rangeVal . Split ( ':' ) ;
171+ if ( rangeValSplit . Length != 0 )
172+ {
173+ if ( rangeValSplit . Length == 1 )
174+ {
175+ var match = _cellRegex . Match ( rangeValSplit [ 0 ] ) ;
176+ if ( match . Success )
177+ {
178+ var row = int . Parse ( match . Groups [ 2 ] . Value ) ;
179+ var column = ColumnHelper . GetColumnIndex ( match . Groups [ 1 ] . Value ) ;
180+ rangeList . Add ( new Range
181+ {
182+ StartColumn = column ,
183+ StartRow = row ,
184+ EndColumn = column ,
185+ EndRow = row
186+ } ) ;
187+ }
188+ }
189+ else
190+ {
191+ var match1 = _cellRegex . Match ( rangeValSplit [ 0 ] ) ;
192+ var match2 = _cellRegex . Match ( rangeValSplit [ 1 ] ) ;
193+ if ( match1 . Success && match2 . Success )
194+ {
195+ rangeList . Add ( new Range
196+ {
197+ StartColumn = ColumnHelper . GetColumnIndex ( match1 . Groups [ 1 ] . Value ) ,
198+ StartRow = int . Parse ( match1 . Groups [ 2 ] . Value ) ,
199+ EndColumn = ColumnHelper . GetColumnIndex ( match2 . Groups [ 1 ] . Value ) ,
200+ EndRow = int . Parse ( match2 . Groups [ 2 ] . Value )
201+ } ) ;
202+ }
203+ }
204+ }
205+ }
206+
207+ yield return new ConditionalFormatRange
208+ {
209+ Node = conditionalFormat ,
210+ Ranges = rangeList
211+ } ;
212+ }
213+ }
214+
156215 private class MergeCellIndex
157216 {
158217 public int RowStart { get ; set ; }
@@ -172,14 +231,44 @@ private class XChildNode
172231 public int RowIndex { get ; set ; }
173232 }
174233
234+ private struct Range
235+ {
236+ public int StartColumn { get ; set ; }
237+ public int StartRow { get ; set ; }
238+ public int EndColumn { get ; set ; }
239+ public int EndRow { get ; set ; }
240+
241+ public bool ContainsRow ( int row )
242+ {
243+ return row >= StartRow && row <= EndRow ;
244+ }
245+ }
246+
247+ private class ConditionalFormatRange
248+ {
249+ public XmlNode Node { get ; set ; }
250+ public List < Range > Ranges { get ; set ; }
251+ }
252+
175253 private void WriteSheetXml ( Stream stream , XmlDocument doc , XmlNode sheetData , bool mergeCells = false )
176254 {
255+ var conditionalFormatRanges = ParseConditionalFormatRanges ( doc ) . ToList ( ) ;
256+ var newConditionalFormatRanges = new List < ConditionalFormatRange > ( ) ;
257+ newConditionalFormatRanges . AddRange ( conditionalFormatRanges ) ;
258+
177259 //Q.Why so complex?
178260 //A.Because try to use string stream avoid OOM when rendering rows
179261 sheetData . RemoveAll ( ) ;
180262 sheetData . InnerText = "{{{{{{split}}}}}}" ; //TODO: bad code smell
181263 var prefix = string . IsNullOrEmpty ( sheetData . Prefix ) ? "" : $ "{ sheetData . Prefix } :";
182264 var endPrefix = string . IsNullOrEmpty ( sheetData . Prefix ) ? "" : $ ":{ sheetData . Prefix } "; //
265+
266+ var conditionalFormatNodes = doc . SelectNodes ( "/x:worksheet/x:conditionalFormatting" , _ns ) ;
267+ for ( var i = 0 ; i < conditionalFormatNodes . Count ; ++ i )
268+ {
269+ var node = conditionalFormatNodes . Item ( i ) ;
270+ node . ParentNode . RemoveChild ( node ) ;
271+ }
183272 var contents = doc . InnerXml . Split ( new string [ ] { $ "<{ prefix } sheetData>{{{{{{{{{{{{split}}}}}}}}}}}}</{ prefix } sheetData>" } , StringSplitOptions . None ) ;
184273 using ( var writer = new StreamWriter ( stream , Encoding . UTF8 ) )
185274 {
@@ -743,6 +832,35 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo
743832 }
744833
745834 enumrowend = newRowIndex - 1 ;
835+
836+ var conditionalFormats = conditionalFormatRanges . Where ( cfr => cfr . Ranges . Any ( r => r . ContainsRow ( originRowIndex ) ) ) ;
837+ foreach ( var conditionalFormat in conditionalFormats ) {
838+ var newConditionalFormat = conditionalFormat . Node . Clone ( ) ;
839+ var sqref = newConditionalFormat . Attributes [ "sqref" ] ;
840+ var ranges = conditionalFormat . Ranges . Select ( r =>
841+ {
842+ if ( r . ContainsRow ( originRowIndex ) )
843+ {
844+ return new Range ( )
845+ {
846+ StartColumn = r . StartColumn ,
847+ StartRow = enumrowstart + 1 ,
848+ EndColumn = r . EndColumn ,
849+ EndRow = enumrowend + 1
850+ } ;
851+ }
852+ else
853+ {
854+ return r ;
855+ }
856+ } ) . ToList ( ) ;
857+ sqref . Value = string . Join ( " " , ranges . Select ( r => $ "{ ColumnHelper . GetAlphabetColumnName ( r . StartColumn ) } { r . StartRow } :{ ColumnHelper . GetAlphabetColumnName ( r . EndColumn ) } { r . EndRow } ") ) ;
858+ newConditionalFormatRanges . Remove ( conditionalFormat ) ;
859+ newConditionalFormatRanges . Add ( new ConditionalFormatRange {
860+ Node = newConditionalFormat ,
861+ Ranges = ranges
862+ } ) ;
863+ }
746864 }
747865 else
748866 {
@@ -790,6 +908,11 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo
790908 writer . Write ( $ "</{ prefix } mergeCells>") ;
791909 }
792910
911+ if ( newConditionalFormatRanges . Count != 0 )
912+ {
913+ writer . Write ( string . Join ( string . Empty , newConditionalFormatRanges . Select ( cf => cf . Node . OuterXml ) ) ) ;
914+ }
915+
793916 writer . Write ( contents [ 1 ] ) ;
794917 }
795918 }
@@ -913,7 +1036,6 @@ private void ReplaceSharedStringsToStr(IDictionary<int, string> sharedStrings, r
9131036 private void UpdateDimensionAndGetRowsInfo ( IDictionary < string , object > inputMaps , ref XmlDocument doc , ref XmlNodeList rows , bool changeRowIndex = true )
9141037 {
9151038 // note : dimension need to put on the top 
916-
9171039 var dimension = doc . SelectSingleNode ( "/x:worksheet/x:dimension" , _ns ) as XmlElement ;
9181040 if ( dimension == null )
9191041 throw new NotImplementedException ( "Excel Dimension Xml is null, please issue file for me. https://github.com/shps951023/MiniExcel/issues" ) ;
0 commit comments