void UpdateLineLayout(bool invalidateAll) { if (this.canvas == null) { // Not yet... no template return; } // Determine number of visible lines this.VisibleLineCount = (int)Math.Ceiling(this.canvas.ActualHeight / this.lineHeight); this.VerticalScrollPageSize = this.VisibleLineCount - 1; this.VerticalScrollRange = this.Buffer.TextData.Lines.Count; var lineNumbers = this.displayedLines.Keys.ToArray(); List<TextLineVisual> recycledLines = null; // Remove any line not in visible range if (invalidateAll) { // Put all currently displayed lines in the recycle bin recycledLines = this.displayedLines.Values.ToList(); this.displayedLines.Clear(); } else { foreach (var line in lineNumbers) { if (line < this.TopVisibleLine || line >= this.TopVisibleLine + this.VisibleLineCount) { // Put the line in the recycle bin, leaving it parented by the canvas. This // greatly reduces visual tree noise. if (recycledLines == null) { recycledLines = new List<TextLineVisual>(); } recycledLines.Add(this.displayedLines[line]); this.displayedLines.Remove(line); } } } // Now update and position each line. for (int i = 0; i < this.VisibleLineCount && i + this.TopVisibleLine < this.Buffer.TextData.Lines.Count; i++) { int lineIndex = i + this.TopVisibleLine; TextLineVisual visual; if (!this.displayedLines.TryGetValue(lineIndex, out visual)) { if (recycledLines != null && recycledLines.Count > 0) { visual = recycledLines[recycledLines.Count - 1]; recycledLines.RemoveAt(recycledLines.Count - 1); visual.SetLineIndex(lineIndex); } else { visual = new TextLineVisual(this, lineIndex); visual.SetBinding(Canvas.LeftProperty, this.horizontalOffsetBinding); this.canvas.Children.Add(visual); } this.displayedLines[lineIndex] = visual; } Canvas.SetTop(visual, i * this.lineHeight); } // Remove any remaining lines in the recycle bin if (recycledLines != null) { foreach (var visual in recycledLines) { this.canvas.Children.Remove(visual); } } PlaceCaretAndSelection(); }
TextLineVisual GetLineVisual(int line) { TextLineVisual visual; if (!this.displayedLines.TryGetValue(line, out visual)) { // Note: This will not be displayed and will just GC away when // the caller is done with it. Still useful for coord/loc mapping. visual = new TextLineVisual(this, line); } return visual; }