how to keep linestyle width = 1 when zoom in diagram?

May 15, 2016 at 3:43 PM
Dear sir,

When I use default line style which width is 1,
when enlarge/zoom in the diagram, the line width is also enlarge,

sometimes, I want to zoom in 1000% of diagram, but line width is too big to see others.
so I want to keep width to 1,
how to do that?

what about text style?

Thanks.
Ray
Coordinator
May 25, 2016 at 8:41 AM
Hello chinaray,

Keeping the line width constant when zooming in would mean you have to derive and implement your own Display component as the standard display component zooms the drawing context before drawing the lines.
This is what DrawDiagram (called by Display.OnPaint) does:

private void DrawDiagram(Rectangle clipRectangle) {
// Zoom drawing context
// Draw Background
// Reset zoom
// Draw grid and diagram border

// Draw shapes and their outlines (selection indicator)
// Zoom drawing context
// Draw Shapes

// Reset zoom
// Draw Handles, caption bounds, etc

// Zoom drawing context
// Draw tool preview
// Reset zoom
}

You would have to reimplement the coordinate transformation or scale the pens used by the LineStyles (cached in class ToolCache) depending on the zoom factor.
Caution:
NShape has several checks on coordinates smaller/bigger than Short.MinValue/Short.MaxValue because GDI+ throws OutOfMemoryExceptions if the coordinate values are too small/big.
Coordinator
May 25, 2016 at 9:26 AM
Forget about the checks on Short.MinValue/Short.MaxValue - these checks were only made when exporting a diagram to a bitmap picture (jpg, png, bmp) because GDI+ cannot handle such huge bitmap images (emf works).
Sorry, my fault.
Coordinator
Nov 11, 2016 at 9:58 AM
Update: More information on the background and a workaround suggestion

NShape transforms the whole graphics context ('canvas'). This means that everything will be enlarged when zooming.

The reason why the selection indicators and resize-/connection points are not enlarged is that the transformation of the graphics context is undone before drawing them. They are drawn to an untransformed 'canvas' to (manually transformed) coordinates.

However, after thinking about it for a while, I think there is a way to implement your requirement without having to change the code of the display component.
The basic idea is to use the IDiagrampresenter interface that was meant for the tools to draw their stuff (selection indicators, previews and graphical hints).
This interface has the methods ResetTransformation and RestoreTransformation.

The interface is implemented by the display component, which also implements the IDisplayService interface for the shapes.
So you can cast the shape's IDisplayService to IDiagramPresenter in order to gain access to the Reset-/RestoreTransformation methods:

Example (Draw method of a polyline shape):

public override void Draw(Graphics graphics) {
if (graphics == null) throw new ArgumentNullException("graphics");
Dataweb.NShape.Controllers.IDiagramPresenter diagramPresenter = DisplayService as Dataweb.NShape.Controllers.IDiagramPresenter;
if (diagramPresenter != null) diagramPresenter.ResetTransformation();

UpdateDrawCache();
(...)
base.Draw(graphics);

if (diagramPresenter != null) diagramPresenter.RestoreTransformation();
}

Next thing you have to do is to transform the point array that will be used for drawing the line to the correct positions.
For this task, you need to know the display's zoom factor which you can get (again) from the display service:
Simply cast it to "Dataweb.NShape.WinFormsUI.Display" and you will have access to the ZoomLevel property (zoom in percentage).
Coordinator
Nov 15, 2016 at 10:42 AM
Edited Nov 15, 2016 at 10:43 AM
Update: After thinking a while about the workaround above, I realized that you don't even need the reference to the display component as the Transform matrix of the graphics context is already transformed collectly. You can simply use it to transform the shape points.

Here is a quick proof of concept for Polyline (GeneralShapes assembly):
public override void DrawOutline(Graphics graphics, Pen pen) {
    IDiagramPresenter diagramPresenter = DisplayService as IDiagramPresenter;
    Point[] origShapePoints = null;
    if (diagramPresenter != null) {
        // Make a backup copy of the original points
        origShapePoints = new Point[shapePoints.Length];
        Array.Copy(shapePoints, origShapePoints, shapePoints.Length);

        // Transform points with the (already correctly transformed) graphics matrix 
        // in order to get the same transformation
        graphics.Transform.TransformPoints(shapePoints);

        // Now reset transformation and draw the (transformed) points
        diagramPresenter.ResetTransformation();
    }

    base.DrawOutline(graphics, pen);

    if (diagramPresenter != null) {
        System.Diagnostics.Debug.Assert(origShapePoints != null);
        // Restore graphics context transformation and shape points
        diagramPresenter.RestoreTransformation();
        Array.Copy(origShapePoints, shapePoints, shapePoints.Length);
    }
}
Remember:
If your line uses line caps, you have to do handle them, too.
The outlines of the caps themselves are drawn by the Pen automatically, the cap background is drawn by the shape (see DrawStartCapBackground/DrawStartEndBackground).