3

I am currently using the following method to find the coordinates of a Range within a document:

private Rectangle GetRangeCoordinates(Window w, Range r)
{
    int left = 0;
    int top = 0;
    int width = 0;
    int height = 0;

    w.GetPoint(out left, out top, out width, out height, r);

    return new Rectangle(left, top, width, height);
}

This works really well unless the Range is off the screen by a fairly large margin (quite a few pages), in which case I get the following exception:

System.Runtime.InteropServices.COMException (0x800A1066): Command failed at Microsoft.Office.Interop.Word.Window.GetPoint(Int32& ScreenPixelsLeft, Int32& ScreenPixelsTop, Int32& ScreenPixelsWidth, Int32& ScreenPixelsHeight, Object obj) at [ProjectName].[TaskpaneName].GetRangeCoordinates(Window w, Range r) in [...somePath...][TaskpaneName].cs:line 66

Is there a way to figure out if a Range is on screen or not, so that I can only call this method when it is?

Leo
  • 5,013
  • 1
  • 28
  • 65
  • To calculate if the selected range is on the document you'll need to know the dimensions of the document. Followed by the clicked starting point of the rectangle. With the dimension of the screen and the starting point of the rectangle you can create a threshold value of how big the selected range can be. – M.B. Mar 15 '19 at 13:02
  • @Innominatum the issue is I can't call `GetPoint` on ranges that are (considerably) off screen because I get a COMException - which I cannot `catch` – Leo Mar 15 '19 at 13:05
  • You can't add a catch try block around the calling method and catch the COMException? – M.B. Mar 15 '19 at 13:20
  • Could you provide us a more detailed version of the COMException? – M.B. Mar 15 '19 at 13:27
  • @Innominatum - I could catch the exception, I was just wondering whether there is a better way... – Leo Mar 15 '19 at 13:35
  • The only possibility I can think of is to use the `Window.ScrollIntoView` method. This brings the specified `Range` into the visible area of the window. – Cindy Meister Mar 15 '19 at 22:40
  • @CindyMeister - i don't want to move the document... – Leo Mar 18 '19 at 10:39

1 Answers1

4

This is how I did this.

I created a few extension methods for Application and Range:

public static class ApplicationExensions
{
    // more (rather than less)
    // does not do headers and footers
    public static Range GetCurrentlyVisibleRange(this Application application)
    {
        try
        {
            Window activeWindow = application.ActiveWindow;
            var left = application.PointsToPixels(activeWindow.Left);
            var top = application.PointsToPixels(activeWindow.Top);
            var width = application.PointsToPixels(activeWindow.Width);
            var height = application.PointsToPixels(activeWindow.Height);
            var usableWidth = application.PointsToPixels(activeWindow.UsableWidth);
            var usableHeight = application.PointsToPixels(activeWindow.UsableHeight);

            var startRangeX = left;// + (width - usableWidth);
            var startRangeY = top;// + (height - usableHeight);

            var endRangeX = startRangeX + width;//usableWidth;
            var endRangeY = startRangeY + height;//usableHeight;

            Range start = (Range) activeWindow.RangeFromPoint((int) startRangeX, (int) startRangeY);
            Range end = (Range) activeWindow.RangeFromPoint((int) endRangeX, (int) endRangeY);

            Range r = application.ActiveDocument.Range(start.Start, end.Start);

            return r;
        }
        catch (COMException)
        {
            return null;
        }
    }
}

public static class RangeExtensions
{
    public static bool Intersects(this Range a, Range b)
    {
        return a.Start <= b.End && b.Start <= a.End;
    }

    public static Rectangle? GetCoordinates(this Range range)
    {
        try
        {
            Application application = range.Application;
            Window window = application.ActiveWindow;

            int left = 0;
            int top = 0;
            int width = 0;
            int height = 0;

            window.GetPoint(out left, out top, out width, out height, range);

            return new Rectangle(left, top, width, height);
        }
        catch (COMException e)
        {
            return null;
        }
    }
}

And then I used them like this:

Range currentlyVisibleRange = application.GetCurrentlyVisibleRange();

if (currentlyVisibleRange.Intersects(rng)){
    var coords = rng.GetCoordinates();
}
Leo
  • 5,013
  • 1
  • 28
  • 65