How to Center and Select a Shape in the Active Window

Started by Thomas Winkel, December 15, 2014, 12:27:27 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Thomas Winkel

Hi,

I  thought there must be a method that does the job...
Any ideas?

Best regards,
Thomas

wapperdude

#1
Hopefully, someone with more programming experience will have better advice, but for now, you might want to check out the DoCmd's,  there's a considerable number of them focused on zooming and can be used for automation.  Might try selecting the desired shape, then run the zoom in command.

http://msdn.microsoft.com/en-us/library/aa342178.aspx

ZoomIn:  DoCmd(1216) <==> visCmdZoomIn

Wapperdude
Visio 2019 Pro

Yacine

#2
Hi Thomas,
The code for centering the view on the shape is as follows.


    Dim pinX As Double
    Dim pinY As Double
    Dim primeX As Double
    Dim primeY As Double
    shp.XYToPage pinX, pinY, primeX, primeY
    ActiveWindow.ScrollViewTo primeX, primeY



For the highlighting, it depends upon what you mean by highlighting (selection, bigger lineweight, color, etc.).

Yacine

Thomas Winkel

#3
Thanks wapperdude, thanks Yacine very helpful!

Your code does not change the page if the shape is on another one, Yacine.
Do you have an Idea how to handle this?
My scenario is that i have a dictionary of shapes to process.
Each shape can be anywhere in the document.

I will experiment with the highlighting and compare the user experience.
I guess it will be selection because this does not change the history.
Otherwise I would prefer red color.

Regards,
Thomas

Yacine

#4
That would be something like


set pg = shp.parent 'I assume shp isn't in a group
pg.activate 'then do the scrolling posted earlier
Yacine

Visio Guy

Ok Guys,

I changed the topic title now that I understand what the problem is. The new title should give you a clue as to the solution.

There are three parts to solve this: switching to the page that contains the shape, selecting the shape, then zooming and panning.

1. Switch the page for the active window

Your code can get very, very picky here, because Visio can have several windows open, some of which aren't even drawing windows (stand-alone stencils, master edit windows, ShapeSheet, etc.). And Visio can have several documents open as well. But I'll try and keep it simple:

    Visio.ActiveWindow.Page = visShp.Page


2. Select the shape:

    Call Visio.ActiveWindow.DeselectAll
    Call Visio.ActiveWindowSelect(visShp, Visio.VisSelectArgs.visSelect)

3. Zoom and pan

To place the shape in the center of the page, you could try something like:

    Visio.ActiveWindow.ViewFit = visio.VisWindowFit.visFitPage or
    Visio.ActiveWindow.ViewFit = visio.VisWindowFit.visFitWidth

But those won't center the shape in the window.

You need to compare the size of the shape to the size of the window, and do all sorts of gyrations to optimize the pan and zoom.

Get and set the view of the window using win.GetViewRect and win.SetViewRect, and you can get the size of the shape using shp.BoundingBox.


Putting it all together, with gory details, you get the code below. Run the TestCentering procedure when you have a document with at least 3 pages and one shape on page 3.


Option Explicit

Public Sub TestCentering()

  '// Get the first shape on page 3
  '//
  '// Make sure you have a 3rd page and a shape
  '// on it!
 
  Dim shp As Visio.Shape
  Set shp = Visio.ActiveDocument.Pages(3).Shapes(1)
  Call CenterShapeInView(shp)
 
End Sub

Public Sub CenterShapeInView(ByRef visShp As Visio.Shape)

  '// Notes:
  '// 'w' stands for 'width' and 'window'
  '// 'h' stands for 'height',
  '// 'l', 't', 'r', 'b' stand for 'left', 'top', 'right', 'bottom'
 
  Dim win As Visio.Window
  Set win = visShp.Application.ActiveWindow
 
  If (win.Type = Visio.VisWinTypes.visDrawing) Then
 
    '// Switch the page in the active window so
    '// that it is the page that visShp is on:
    win.Page = visShp.ContainingPage
   
    '// Select the shape:
    Call win.DeselectAll
    Call win.Select(visShp, Visio.VisSelectArgs.visSelect)
   
    '// Zoom and pan:
   
    '// Get the dimensions of the active window. These
    '// are PAGE coordinates for what is displayed in the
    '// window:
    Dim wl As Double, wt As Double, ww As Double, wh As Double
    Call win.GetViewRect(wl, wt, ww, wh)
   
    '// Get the size and position of the shape:
    Dim flags As Integer
    flags = Visio.VisBoundingBoxArgs.visBBoxUprightWH
   
    '// Get the left, bottom, right, top of the shape, and
    '// calculate the center point:
    Dim sl As Double, sb As Double, sr As Double, st As Double
    Dim sx As Double, sy As Double
    Call visShp.BoundingBox(flags, sl, sb, sr, st)
    sx = (sl + sr) * 0.5
    sy = (st + sb) * 0.5
           
    '// Calculate the width and height:
    Dim sw As Double, sh As Double
    sw = Math.Abs(sr - sl)
    sh = Math.Abs(st - sb)
   
    '// Add some padding around the shape, because win.SetViewRect
    '// doesn't seem to be exact when we try to zoom. Let's take
    '// a percentage of the shape's size:
    Const PaddingPct# = 0.05 '//...5% padding
    Dim padding As Double
    If (sw > sh) Then
      padding = PaddingPct * sw
    Else
      padding = PaddingPct * sh
    End If
   
    '// Recalculate the shape dimensions with the added padding:
    sl = sl - padding
    sr = sr + padding
    st = st + padding
    sb = sb - padding
    sw = sr - sl
    sh = st - sb
   
    '// Calculate aspect rations of window and shape.
    '// Greater-than-one means 'more wide than tall':
    Dim arw As Double, ars As Double
    arw = ww / wh
    ars = sw / sh
   
   
    '// We will maximize the view of the shape in the
    '// window. This means that either the sides of the shape
    '// or the shape's top and bottom will be against the
    '// edges of the window. We have to figure this out using
    '// geometry.
   
    '// We'll set up some new dimensions for the window's view:
    Dim wlNew As Double, wtNew As Double, wrNew As Double, wbNew As Double
    Dim wwNew As Double, whNew As Double
   
    If (ars > arw) Then
     
      '// The shape is more 'wider' than the window.
     
      '// The window's left and right should match that
      '// of the shape:
      wlNew = sl
      wrNew = sr
      wwNew = wrNew - wlNew '//...the new view width
     
      '// Thew window's top and bottom SHOULD be calculated,
      '// but since the size of the window won't really change,
      '// we COULD enter nonsense values. But we'll try and
      '// calculate anyway:
      whNew = wwNew / arw '//...width/(width/height) => height
      wtNew = sy + wwNew * 0.5
      wbNew = sy - wwNew * 0.5
     
    Else
   
      '// The window is more 'wider' than the shape.
     
      '// The window's top and bottom should match that
      '// of the shape:
      wtNew = st
      wbNew = sb
      whNew = wtNew - wbNew '//...the new view height
     
      '// Thew window's left and right must be calculated:
      wwNew = arw * whNew '//...(width/height)*height => width
      wlNew = sx - wwNew * 0.5
      wrNew = sx + wwNew * 0.5
     
    End If
   
    '// Set the new window view:
    Call win.SetViewRect(wlNew, wtNew, wwNew, whNew)
   
  Else
    '// Make the active window a ShapeSheet window to
    '// make this happen:
    Call MsgBox("Active window is not a drawing window, I'm not sure what to do!")
  End If

End Sub

For articles, tips and free content, see the Visio Guy Website at http://www.visguy.com
Get my Visio Book! Using Microsoft Visio 2010

Thomas Winkel

Wow, exactly this is what I was looking for, thanks for your complete turn-key solution, Visio Guy!

Thanks Yacine, I didn't know that the parent of a shape is the page, when it is not in a group.
Still so many things to learn...

Thomas Winkel

My colleague gave me a hint to another approach that is implemented here:
http://dmitryivanov.net/2012/11/13/making-wiring-diagrams-in-visio/

Here is the slightly modified code extract which gives a really smooth behavior:

Sub gotoShape(shp As Visio.Shape)
    Dim CenterSelectionOnZoom As Boolean
   
    If Not ActiveWindow.Page = shp.ContainingPage Then
        ActiveWindow.Page = shp.ContainingPage
    End If
   
    ActiveWindow.Select shp, Visio.visDeselectAll + Visio.visSubSelect
    CenterSelectionOnZoom = Application.Settings.CenterSelectionOnZoom
    Application.Settings.CenterSelectionOnZoom = True
    ActiveWindow.Zoom = ActiveWindow.Zoom
    Application.Settings.CenterSelectionOnZoom = CenterSelectionOnZoom
End Sub


Note that all the plausibility checks (explained by Chris above) are missing here.

charlykuntz

Hello all,

isn't it much easier to use the Window.CenterViewOnShape method for centreing the view? This method was added to the Version 2010.

Charly.

Thomas Winkel

#9
Excellent, Charly, works like a charm.
Many thanks!

Too bad, Dmitry's solution was really clever...  ;)

Visio Guy

Wow, I totally missed Window.CenterViewOnShape! This will be good for quick-and-dirty solutions.

But it still doesn't calculate a window-filling zoom percentage like my code attempts to do, but it does make things a lot easier, because now you can get the centered view, then increase or decrease the zoom percentages as needed.
For articles, tips and free content, see the Visio Guy Website at http://www.visguy.com
Get my Visio Book! Using Microsoft Visio 2010

vrouwtjeskip3

Great info here! Some real professionals!
I also used the code, and it works! But after a while (when I draw new data graphics) he just zooms in to a random data graphic.
I already find something to give a data graphic a unique ID, but don't know how to do that so I'm stuck....

Greetz Henri

Visisthebest

The link in Thomas Winkel's post seems to contain something entirely different now:

QuoteMy colleague gave me a hint to another approach that is implemented here:
http://dmitryivanov.net/2012/11/13/making-wiring-diagrams-in-visio/
Visio 2021 Professional