public class TileCanvas : Canvas { public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register( "ImageSource", typeof(ImageSource), typeof(TileCanvas), new PropertyMetadata(null, ImageSourceChanged)); private Size _lastActualSize; public TileCanvas() { LayoutUpdated += OnLayoutUpdated; } public ImageSource ImageSource { get { return (ImageSource)GetValue(ImageSourceProperty); } set { SetValue(ImageSourceProperty, value); } } private void OnLayoutUpdated(object sender, object o) { var newSize = new Size(ActualWidth, ActualHeight); if (_lastActualSize == newSize) return; _lastActualSize = newSize; Rebuild(); } private static void ImageSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) { var self = (TileCanvas)o; var src = self.ImageSource; if (src == null) return; var image = new Image { Source = src }; image.ImageOpened += self.ImageOnImageOpened; image.ImageFailed += self.ImageOnImageFailed; //add it to the visual tree to kick off ImageOpened self.Children.Add(image); } private void ImageOnImageFailed(object sender, ExceptionRoutedEventArgs exceptionRoutedEventArgs) { ManageImageEvent((Image)sender); Children.Add(new TextBlock { Text = exceptionRoutedEventArgs.ErrorException.Message, Foreground = new SolidColorBrush(Colors.Red) }); } private void ImageOnImageOpened(object sender, RoutedEventArgs routedEventArgs) { ManageImageEvent((Image)sender); Children.Clear(); Rebuild(); } private void ManageImageEvent(Image image) { image.ImageOpened -= ImageOnImageOpened; image.ImageFailed -= ImageOnImageFailed; } private void Rebuild() { var bmp = ImageSource as BitmapSource; if (bmp == null || bmp.PixelWidth == 0 || bmp.PixelHeight == 0 || ActualWidth.Equals(0) || ActualHeight.Equals(0)) return; double currentTiledWidth = -1; double currentTiledHeight = -1; foreach (var child in Children.Where(c => c is Image).Cast()) { var childTop = GetTop(child); var childLeft = GetLeft(child); if (childLeft >= ActualWidth || childTop >= ActualHeight) Children.Remove(child); else { currentTiledWidth = Math.Max(childLeft, currentTiledWidth); currentTiledHeight = Math.Max(childTop, currentTiledHeight); } } for (var x = 0; x < ActualWidth; x += bmp.PixelWidth) for (var y = 0; y < ActualHeight; y += bmp.PixelHeight) if (x > currentTiledWidth || y > currentTiledHeight) { var image = new Image { Source = ImageSource }; SetLeft(image, x); SetTop(image, y); Children.Add(image); } Clip = new RectangleGeometry { Rect = new Rect(0, 0, ActualWidth, ActualHeight) }; } }