Search
User Defined Node Shapes and Custom Drawing

The appearance of WpfDiagram items can be customized in several additional ways, as described below.

Shape Templates

The Shape class provides the means for defining complex node shapes, made of straight lines, arcs and Bézier curves. A shape template must always contain an outline to be used for hit testing, clipping and finding intersections with other items. Optionally shapes can contain decoration elements and text region definitions. Decorations are visual elements that do not take part in hit testing and clipping. Text regions are the parts of shapes in which node's text is laid out and rendered. WpfDiagram provides a set of 87 predefined shape templates, which can be accessed by the Shapes collection or FromId method of the Shape class. Each predefined shape has a string identifier assigned to it. Use the FromId method to find a shape with the specified id. A shape template can be assigned to the Shape member of shape nodes.

Dynamic Shapes

Shapes defined via ElementTemplate objects are scaled proportionally to the size of nodes. If shape elements should scale non-uniformly, use the Shape(formula) constructor to define shapes through Visio-like formulas that take into consideration the current width and height of nodes. Another Shape constructor takes a CreatePathDelegate parameter that can be used to define shapes via .NET functions.

A formula shape can be parameterized by adding ShapeControlPoint objects to the shape's ControlPoints collection. The control point positions are passed as arguments to the shape scripts, where they can be used to modify the node's appearance. For example, the following code defines a rounded rectangle shape, whose corner radius can be modified by users via the control point.

C#  Copy Code

// a rounded rectangle shape, with an arc at each corner
string roundRect = @"
    r = Min(Width / 2, radius.X);
    MoveTo(r, 0);
    LineTo(Width - r, 0);
    ArcTo(Width, r, false, true, r, r);
    LineTo(Width, Height - r);
    ArcTo(Width - r, Height, false, true, r, r);
    LineTo(r, Height);
    ArcTo(0, Height - r, false, true, r, r);
    LineTo(0, r);
    ArcTo(r, 0, false, true, r, r);
    ";

var myRect = new Shape(roundRect, "MyRect");

// add a control point for the 'radius' parameter
myRect.ControlPoints.Add(new ShapeControlPoint(
    "radius", 15, 5, 45, UnitType.Fixed, 0, 0, 0, UnitType.Fixed));

Shape Libraries

The ShapeDesigner tool included in the WpfDiagram suite lets you draw custom shapes and store them in shape libraries. A library file can be loaded into your application using the ShapeLibrary class. The definitions loaded from a shape library are automatically added to the Shapes collection and can be accessed through the FromId method, just as the predefined shapes.

Custom Painting

Another way to customize item's appearance is implemented by raising an event each time an item is painted, giving you the option to attach your own drawing code to the WpfDiagram's rendering engine. Use the corresponding CustomDraw property of items to enable that feature in your application.

WpfDiagram provides several options for custom drawing. If you choose Additional drawing style, your drawing code will be executed after the standard item drawing code is executed and Image is rendered, but before the text is rendered. Furthermore, the DrawingContext object you receive to draw on has a clipping region applied that corresponds to a node's shape. If using Full drawing mode, none of the standard WpfDiagram drawing code is executed - just yours. The ShadowOnly mode performs standard painting of items, but leaves the drawing of shadows to your code.

Place your own drawing code in a handler of the DrawNode, DrawCell and DrawLink events. These events are fired for each custom-drawn item that needs repainting.

Combining Custom Shapes with Custom Drawing

Finally, you could combine user-defined shapes with the Additional custom-painting style. Thus, you can get custom shapes enabled for a node and your custom painting code executing with the node shape used as clipping region. You might clear the clipping region, but be careful - if you draw outside node's bounding rectangle the view may not update correctly.

Shape control points

Shape formulas can now be parameterized by associating control points with Shape objects. Each control point is passed to the shape script as a named variable. Apart from the name, you can specify the default, min and max coordinates for each parameter via the ShapeControlPoint constructor, and whether to treat its values as percents or fixed offset.

For example, the following code creates an arrow shape with adjustable arrowhead and fletching sizes:

C#  Copy Code

string arrow60deg = @"
  x1 = p1.X;
  y1 = p1.Y;
  x2 = IIf(Width>=2*Height,0.5*Height*Tan(PI/6), 0.15*Width);
  x3 = IIf(Width>=2*Height, Height/4, 0.15*Width);
  MoveTo(0, Height * 0.5);
  LineTo(x1 , Height);
  LineTo(x1, Height - y1);
  LineTo(Width -x3 -x2 -x2, Height - y1);
  LineTo(Width -x3 -x2, Height);
  LineTo(Width, Height);
  LineTo(Width * p2.X / 100, Height * p2.Y / 100);
  LineTo(Width, 0);
  LineTo(Width - x3 -x2, 0);
  LineTo(Width -x3 -x2 -x2, y1);
  LineTo(x1, y1);
  LineTo(x1, 0);
  LineTo(0, Height * 0.5);
  ";

diagram.DefaultShape = new Shape(arrow60deg, "", new List<ShapeControlPoint>
{
  new ShapeControlPoint("p1",
    20, 0, -10, UnitType.Fixed,
    20, 0, 100, UnitType.Fixed) { MaxXRelativeToWidth = true },
  new ShapeControlPoint("p2",
    80, 60, 100, UnitType.Percentage,
    50, 50, -50, UnitType.Percentage) { MaxYRelativeToHeight = true }
}, "arrow60deg");