public override void Invalidate(Rectangle rect) { if (!Control.IsLoaded) { if (Widget.Loaded) { Application.Instance.AsyncInvoke(() => Invalidate(rect)); } return; } if (tiled) { foreach (var tile in visibleTiles.Values) { if (tile.Bounds.Intersects(rect)) { tile.InvalidateVisual(); } } } else if (OptimizedInvalidateRect) { if (((rect.Width * rect.Height) / (Control.ActualWidth * Control.ActualHeight)) > 0.9) { Invalidate(); return; } if (invalidateTiles == null) { invalidateTiles = new List <EtoTile>(); } var overlappingTiles = new List <EtoTile>(); foreach (var overlappingTile in invalidateTiles) { if (rect == overlappingTile.Bounds) { overlappingTile.InvalidateVisual(); return; } else if (rect.Intersects(overlappingTile.Bounds)) { rect.Union(overlappingTile.Bounds); overlappingTiles.Add(overlappingTile); } } EtoTile tile; if (unusedTiles.Count > 0) { tile = unusedTiles[unusedTiles.Count - 1]; tile.Bounds = rect; tile.Visibility = sw.Visibility.Visible; unusedTiles.Remove(tile); } else { tile = new EtoTile { Handler = this, SnapsToDevicePixels = true }; tile.Bounds = rect; Control.Children.Add(tile); } invalidateTiles.Add(tile); foreach (var overlappingTile in overlappingTiles) { overlappingTile.Visibility = sw.Visibility.Collapsed; invalidateTiles.Remove(overlappingTile); unusedTiles.Add(overlappingTile); } } else { base.Invalidate(rect); } }
void UpdateTiles(bool rebuildKeys = false) { if (!tiled || !Control.IsLoaded) { return; } var controlSize = new Size((int)Control.ActualWidth, (int)Control.ActualHeight); var rect = new Rectangle(controlSize); var scroll = scrollable; if (scroll != null) { // only show tiles in the visible rect of the scrollable var visibleRect = new Rectangle(scroll.ClientSize); var scrollableHandler = (ScrollableHandler)scroll.Handler; visibleRect.Offset(-Control.TranslatePoint(new sw.Point(), scrollableHandler.ContentControl).ToEtoPoint()); rect.Intersect(visibleRect); } // cache unused tiles and remove them from the visible tiles list var keys = visibleTiles.Keys.ToArray(); for (int i = 0; i < keys.Length; i++) { var key = keys[i]; var tile = visibleTiles[keys[i]]; if (!tile.Bounds.Intersects(rect)) { visibleTiles.Remove(key); // keep tile, but make it invisible when needed later tile.Visibility = sw.Visibility.Collapsed; unusedTiles.Add(tile); } } // rebuild keys (e.g. when the size of the control changes) if (rebuildKeys) { RebuildKeys(); } // calculate tile range that is visible var top = rect.Top / tileSize.Height; var bottom = (rect.Bottom + tileSize.Height - 1) / tileSize.Height; var left = rect.Left / tileSize.Width; var right = (rect.Right + tileSize.Width - 1) / tileSize.Width; // make sure all needed tiles are created/visible for (var y = top; y < bottom; y++) { for (var x = left; x < right; x++) { var position = new Point(x, y); if (!maxTiles.Contains(position)) { continue; } var key = position.Y * maxTiles.Width + position.X; // calculate bounds of tile var xpos = x * tileSize.Width; var ypos = y * tileSize.Height; var xsize = Math.Min(tileSize.Width, controlSize.Width - xpos); var ysize = Math.Min(tileSize.Height, controlSize.Height - ypos); if (xsize < 0 || ysize < 0) { continue; } var bounds = new Rectangle(xpos, ypos, xsize, ysize); EtoTile tile; if (!visibleTiles.TryGetValue(key, out tile)) { tile = unusedTiles.FirstOrDefault(); if (tile != null) { // use existing cached tile and make it visible again unusedTiles.Remove(tile); tile.Position = position; tile.Bounds = bounds; tile.Visibility = sw.Visibility.Visible; tile.InvalidateVisual(); } else { // need a new tile, no cached ones left tile = new EtoTile { Handler = this, SnapsToDevicePixels = true, Position = position, Bounds = bounds }; Control.Children.Add(tile); } // set tile as visible visibleTiles[key] = tile; } else { tile.Bounds = bounds; tile.InvalidateVisual(); } } } }