private ViewPortState CalculateState() { Logger.Debug(GetType(), "CalculateState()'s input HasRowHeightPx?={0} tbodyFactHeightPx={1} theadFactHeight={2} allItemsCount={3} rowHeightPx={4} topScrollPosPx={5}", HasRowHeightPx, _view.BodyVisibleSpace, _view.HeaderVisibleSpace, _model.Items.Length + _groups.Count * RowsPerGroupDescr, RowHeightPx, _view.ScrollPosition ); var result = new ViewPortState { RawScrollPos = _view.ScrollPosition, PhysicalRowsCount = _model.Items.Length, VirtualRowsCount = _groups.Count * RowsPerGroupDescr, AllRowsCount = _model.Items.Length + _groups.Count * RowsPerGroupDescr, TheoreticalVisibleRowsCount = (int)ComputeMaxVisibleRows(), FirstVisibleRow = (int)ComputeFirstVisibleRow(), IncompleteLeadRowPad = ComputeIncompleteLeadRowPad(), IncompleteBodyPad = ComputeIncompleteBodyRowPad() }; Logger.Debug(GetType(), "CalculateState()'s raw outcome={0}", result); if (result.FirstVisibleRow > result.AllRowsCount || result.FactVisibleRowsCount < 0) { //scroll is past data (probably many rows were deleted / grouping changed) result.IsInvalidScrollPos = true; if (result.AllRowsCount >= result.TheoreticalVisibleRowsCount) { //will scroll to real bottom of new view result.FirstVisibleRow = result.AllRowsCount - result.TheoreticalVisibleRowsCount; result.RawScrollPos = result.FirstVisibleRow * RowHeightPx; } else { //will just move to beginning of table result.FirstVisibleRow = 0; result.RawScrollPos = 0; } Logger.Debug(GetType(), "CalculateState()'s adjusted outcome={0}", result); } return(result); }
private void PopulateRows(bool itemsChanged, bool shouldRestoreScrollPosition = false) { var oldState = FormerState ?? ViewPortState.CreateEmpty(); Logger.Debug(GetType(), "Former state: {0} shouldRestoreScrollPosition={1}", oldState, shouldRestoreScrollPosition); if (InitializeRowHeightIfNeededAndPossible()) { UpdateTbodyHeight(); } if (shouldRestoreScrollPosition && oldState.IsEmpty) { shouldRestoreScrollPosition = false; } var newState = shouldRestoreScrollPosition ? oldState : CalculateState(); if (shouldRestoreScrollPosition || newState.IsInvalidScrollPos) { _view.ScrollPosition = oldState.RawScrollPos; } Logger.Debug(GetType(), "New state: {0}", newState); Logger.Debug(GetType(), "Scrolled progressed rows: {0} rowHeight={1}", newState.FirstVisibleRow - oldState.FirstVisibleRow, RowHeightPx); //see if any rows can be preserved by checking whether windows overlap var overlap = new Overlap( itemsChanged, oldState, newState); Logger.Debug(GetType(), "calculated overlap: {0}", overlap); //delete not overlapped leading rows (present in old BUT not present in new) if (overlap.ToPreTrim.IsNonEmpty) { Logger.Debug(GetType(), "overlap - removing unneccessary {0} leading rows", overlap.ToPreTrim.Length); for (var i = overlap.ToPreTrim.Length; i > 0; i--) { DestroyRowAt(0); } } //delete not overlapped trailing rows if (overlap.ToPostTrim.IsNonEmpty) { Logger.Debug(GetType(), "overlap - removing unneccessary {0} trailing rows", overlap.ToPostTrim.Length); for (var i = overlap.ToPostTrim.Length; i > 0; i--) { var pos = _view.BodyRowCount - 1; DestroyRowAt(pos); } } //prepend not overlapped rows if (overlap.ToPrepend.IsNonEmpty) { Logger.Debug(GetType(), "overlap - prepending new {0} leading rows", overlap.ToPrepend.Length); var idx = 0; foreach (var item in EnumerateRecords(overlap.ToPrepend).AsEnumerable()) { switch (item.Item1) { case DataOrGroup.Data: Logger.Debug(GetType(), "Prepending item {0}", item.Item2); InsertRowFor(idx++, item.Item2); break; case DataOrGroup.Group: Logger.Debug(GetType(), "Prepending group trailer for grp {0}", item.Item1); InsertGroupTrailer(idx++, item.Item3); break; default: throw new Exception("unsupported DataOrGroup"); } } } //append not overlapped rows if (overlap.ToAppend.IsNonEmpty) { Logger.Debug(GetType(), "overlap - appending new {0} trailing rows", overlap.ToAppend.Length); foreach (var item in EnumerateRecords(overlap.ToAppend).AsEnumerable()) { switch (item.Item1) { case DataOrGroup.Data: Logger.Debug(GetType(), "Appending item {0}", item.Item2); AppendRowFor(item.Item2); break; case DataOrGroup.Group: Logger.Debug(GetType(), "Appending group trailer for grp {0}", item.Item3); AppendGroupTrailer(item.Item3); break; default: throw new Exception("unsupported DataOrGroup"); } } } var padBodyToBottom = newState.FirstVisibleRow == 0 || newState.LastVisibleRow != (_model.Items.Length + _groups.Count * RowsPerGroupDescr - 1); Logger.Debug(GetType(), "Former space lead={0} trail={1}", _view.LeadSpace, _view.TrailSpace); _view.FirstRowIndex = newState.FirstVisibleRow; _view.LeadSpace = newState.FirstVisibleRow * RowHeightPx + (padBodyToBottom ? 0 : newState.IncompleteBodyPad + newState.IncompleteLeadRowPad); _view.TrailSpace = (newState.AllRowsCount - newState.FirstVisibleRow - newState.FactVisibleRowsCount) * RowHeightPx + (padBodyToBottom ? newState.IncompleteBodyPad + newState.IncompleteLeadRowPad : 0); Logger.Debug(GetType(), "New space lead={0} trail={1} FirstVisibleRow={2}", _view.LeadSpace, _view.TrailSpace, newState.FirstVisibleRow); FormerState = newState; //_visRows.DebugPrintContents(); //((HtmlTableBasedTableView)_view).DebugPrintContents(1); }