tags:

views:

2128

answers:

3

I need to draw gridlines on the background of a canvas that will have other controls placed on it.

I tried creating a StreamGeometry, using that to draw lines, and getting that assigned to a DrawingBrush. However I find that if the StreamGeometry has too many lines, the program becomes sluggish after the DrawingBrush is assigned to the Canvas.

Is there anyway of 'pre-rendering' grid lines and having that assigned to a Canvas?

I tried Freeze()ing the brush and geometry but that didn't seem to work. What other options do I have?

Here's my code:

public void RenderGrid()
{
    this.UpdateGrid();
    Pen grid_pen = new Pen(Brushes.Blue, 0.1);
    StreamGeometry sg = new StreamGeometry();

    DrawingBrush b = new DrawingBrush();
    GeometryDrawing gd = new GeometryDrawing();
    gd.Geometry = sg;
    gd.Pen = grid_pen;
    b.Drawing = gd;

    StreamGeometryContext ctx = sg.Open();
    foreach (double d in this.VerticalGrids)
    {

        ctx.BeginFigure(new Point(d, 0), true, false);
        ctx.LineTo(new Point(d, this.RenderSize.Height), true,false);
    }
    foreach (double d in this.HorizontalGrids)
    {

        ctx.BeginFigure(new Point(0, d), true, false);
        ctx.LineTo(new Point(this.RenderSize.Width, d),true, false);
    }
    ctx.Close();
    sg.Freeze();
    gd.Freeze();

    b.Freeze();
    this.Background = b;
}
A: 

One solution is to override OnRender(DrawingContext dc) method on the panel and draw the lines in there.

Another option is to have a lightweight visual element that draws the lines. This element should be added to the visual tree of the panel. The standard WPF Grid control uses a similar approach to draw lines between rows and columns when ShowGridLines property is set to true.

ligaz
+4  A: 

My advice would be to keep the primitive count down. You're making WPF spend CPU cycles and bandwidth creating and sending that massive grid to the render thread and GPU.

DrawingBrush is a TileBrush.

Draw a single unit of the grid onto the DrawingImage and use the TileMode, Viewbox and Viewport properties to tile the grid.

+2  A: 

The solution offered by Ifeanyi Echeruo worked very nicely for me. However, I also needed the grid to scale dynamically. Rather than rebuild the brush each time, I used a single xaml-defined brush for the background of my element, then implemented some hooks to rescale that brush each time the grid lines needed to be resized.

Using the LocalValueEnumerator on the element's Background property, I update the brush's "Viewport" property each time a rescale is needed

<DrawingBrush x:Key="MyBrush" TileMode="Tile" Stretch="Fill" 
  Viewport="0,0,250,250" ViewportUnits="Absolute">


LocalValueEnumerator props = Background.GetLocalValueEnumerator();
while (props.MoveNext())
{
  DependencyProperty prop = props.Current.Property;
  if ((prop.Name == "Viewport") && (!prop.ReadOnly))
  {
    Background.SetValue(prop, new Rect(0, 0, m_ColumnWidth, m_RowHeight));
  }
}
mcdrewski