Recently, one of our customers came up with a very interesting requirement in Spread for .Net control : that of a Scrollable BackgroundImage as in MS Excel.

The default behavior of BackgroundImage in Spread is that it is non scrollable i.e it remains intact and is not repeated while the Spread's data area scrolls.

However, in MS Excel when we add a background image, the image gets 'Tiled' so that it fills the complete data area of the sheet over and over again and hence gives the scrolling effect to the image when the data area scrolls.

It's basically a .Net design behavior that the background image is not scrollable when the ImageLayout is not 'Tiled'. In order to simulate MS Excel's behavior of Scrollable BackgroungImage in Spread for .Net control we need to customize the Spread control by:-

  • Adding support for Custom BackgroundImage for each sheet

  • Adding support for Painting with Layout (Tile, Stretch, Zoom, Center, None)


Further we would be overriding the OnPaintBackground() method of FpSpread class to give the 'Tiled' layout to the Spread's BackgroundImage.

Here is the code for the same:-


protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
ImageSheetView sv = this.ActiveSheet as ImageSheetView;

if (sv != null)
{
Color backColor = sv.BackColor;
Image image = sv.BackgroundImage;
ImageLayout layout = sv.BackgroundImageLayout;
if (backColor == Color.Empty && image == null)
return;
GraphicsState state = e.Graphics.Save();

Brush br = backColor != Color.Empty && layout == ImageLayout.Tile ? new SolidBrush(backColor) : null;
MethodInfo drawBackgroundImage = typeof(ControlPaint).GetMethod("DrawBackgroundImage", BindingFlags.NonPublic | BindingFlags.Static, null
, new Type[] {typeof(Graphics),typeof(Image),typeof(Color),typeof(ImageLayout),
typeof(Rectangle),typeof(Rectangle),typeof(Point),typeof(RightToLeft) }, null);

int rViewportCnt = this.GetRowViewportCount(), cViewportCnt = this.GetColumnViewportCount();
for (int rowViewport = 0; rowViewport < rViewportCnt; rowViewport++)
for (int colViewport = 0; colViewport < cViewportCnt; colViewport++)
{
int topRow, leftCol;
if ((topRow = this.GetViewportTopRow(rowViewport)) < sv.RowCount - 1 && (leftCol = this.GetViewportLeftColumn(colViewport)) < sv.ColumnCount - 1) // Implemented it for total no. of rows and columns rather than a cell range
{
int n;
Rectangle vpRect = this.GetViewportRectangle(rowViewport, colViewport);
bool needOffsetPoint = image != null && layout == ImageLayout.Tile;
Point scrollOffset = vpRect.Location;
if (needOffsetPoint && topRow > 0)
for (n = 0; n < topRow; n++)
scrollOffset.Y -= (int)(sv.GetRowHeight(n) * this.ZoomFactor);
if (needOffsetPoint && leftCol > 0)
for (n = 0; n < leftCol; n++)
scrollOffset.X -= (int)(sv.GetRowHeight(n) * this.ZoomFactor);
if (br != null)
e.Graphics.FillRectangle(br, vpRect);
drawBackgroundImage.Invoke(null, new object[] { e.Graphics, image, backColor, layout, vpRect, vpRect, scrollOffset, this.RightToLeft });
e.Graphics.Restore(state);
}
}
if (br != null)
br.Dispose();
e.Graphics.Restore(state);
}
}


Here is the final output :-

TiledBackGroundImage

Download sample in C#/ VB for complete implementation of the same:-

DownloadSample_CS
DownloadSample_VB