10

First of all, basically I believe I just need help in figuring out how to calculate it, math isnt really my strong side.

FYI, the MS-Word is most likely not relevant to solve my problem, I basically just mention it so you know the context I am in, but I believe it should be solvable also for people who have no knowledge about MS-Word.

I have the problem that Word has the property VerticalPercentScrolled which is an integer.

Which isnt as accurate as I need it to be, consider a Word Document with 300 pages and the scrollbar can only have a integer between 0 - 100 (percent), so for example 25 percent of a 300 page document is a rather big range.

I have now gone and read the correct value, with the help of UI Automation library - TestStack.White - and can successfully set it to its old value, if some un-desired scrolling happens.

Like this:

var hWnd = (IntPtr)WordUtils.GetHwnd();
var processId = Process.GetCurrentProcess().Id;

var uiApp = TestStack.White.Application.Attach((int)processId);
var uiWindow = uiApp.GetWindow(WindowHandling.GetWindowText(hWnd), InitializeOption.NoCache);

var wf = (IUIItemContainer)uiWindow.Get(SearchCriteria.ByClassName("_WwF"));
var wbs= wf.GetMultiple(SearchCriteria.ByClassName("_WwB"));
if (wbs.Length != 1)
    return;

var wb= (IUIItemContainer)wbs.First();
var wgs = wb.GetMultiple(SearchCriteria.ByClassName("_WwG"));
if (wgs.Length != 1)
    return;

var wg = wgs.First();
var element = wg.AutomationElement;

var oldVerticalPercent = (double)element.GetCurrentPropertyValue(ScrollPattern.VerticalScrollPercentProperty);

with this code I get a percent value of lets say 9.442248572683356 instead of 9.

To reset the value I use the following code:

object scrollPattern;
if (element.TryGetCurrentPattern(ScrollPattern.Pattern, out scrollPattern))
    ((ScrollPattern)scrollPattern).SetScrollPercent(ScrollPattern.NoScroll, oldVerticalPercent);

This works like a charm.

Now my problem is what if my document gets larger/smaller during the time I stored the oldValue and want to reapply it, my oldValue needs to be adjusted.

I can read the following values (at least those I found so far):

  1. ScrollPattern.VerticalScrollPercentProperty
  2. ScrollPattern.VerticalViewSizeProperty

I can also go and look for the scrollbar it self and read the following values:

  1. RangeValuePattern.LargeChangeProperty
  2. RangeValuePattern.MaximumProperty
  3. RangeValuePattern.MinimumProperty
  4. RangeValuePattern.SmallChangeProperty
  5. RangeValuePattern.ValueProperty

To look for the scrollbar, I use the following code:

var hWnd = (IntPtr)WordUtils.GetHwnd();
var processId = Process.GetCurrentProcess().Id;

var uiApp = Ts.Application.Attach((int)processId);
var uiWindow = uiApp.GetWindow(WindowHandling.GetWindowText(hWnd), InitializeOption.NoCache);

var wf = (IUIItemContainer)uiWindow.Get(SearchCriteria.ByClassName("_WwF"));
var wbs = wf.GetMultiple(SearchCriteria.ByClassName("_WwB"));
if (wbs.Length != 1)
    return;

var wb = (IUIItemContainer)wbs.First();
var wgs= wb.GetMultiple(SearchCriteria.ByClassName("_WwG"));
if (wgs.Length != 1)
    return;

var nUiScrollBars = wgs.GetMultiple(SearchCriteria.ByClassName("NUIScrollbar"));
if (scrollBar.Length != 1)
    return;

var nUiScrollBar = (IUIItemContainer)nUiScrollBars.First();
var nUiHwndElements = nUiScrollBar.GetMultiple(SearchCriteria.ByClassName("NetUIHWNDElement"));
if (nUiHwndElements.Length != 1)
    return;

var nUiHwndElement = (IUIItemContainer)nUiHwndElements.First();
var netUiScrollBar = nUiHwndElement.GetElement(SearchCriteria.ByClassName("NetUIScrollBar"));

var scrollBarValue = (double)netUiScrollBar.GetCurrentPropertyValue(RangeValuePattern.ValueProperty);

You may ask yourself, why I dont set the RangeValuePattern.ValueProperty when I am able to access it, the problem there is that Word doesnt update the document when I change this property, the scroll thumb does move but the document doesnt move an inch.

I have to set the ScrollPattern.VerticalScrollPercentProperty for it to work.

So my question, is how do I calculate the new ScrollPattern.VerticalScrollPercentProperty based on the oldValue, where the document could shrink or grow in size in between?

Edit:

Here is a testscenario:

scrollPercent       64.86486486486487
scrollViewSize      74.394463667820062
scrollBarLargeChange    37197
scrollBarMaximum        12803
scrollBarMinimum        0
scrollBarSmallChange    1
scrollBarValue      8304

After inserting 5 new pages

scrollPercent       87.890366182251867 <-- undesired scrolling occured (see desired values below)
scrollViewSize      9.442248572683356
scrollBarLargeChange    4721
scrollBarMaximum        45279
scrollBarMinimum        0
scrollBarSmallChange    1
scrollBarValue      39795 <-- undesired scrolling occured (see desired values below)

And as said, I need to adjust the scrollPercent - after inserting those pages - so that it is again on the old position.

I can tell you that in this testscenario, I would need the new value to be

scrollPercent       2.3278413357480452

after changing it to the desired percentValue the scrollBarValue is the only value getting updated and it is than at

scrollBarValue      1054
Rand Random
  • 7,300
  • 10
  • 40
  • 88
  • Are you sure the new value of scrollPercent should be 2.32..., I think it should be 18.339627642. If it is 2.32, how did you get that value? – Rob Anthony Aug 12 '17 at 09:46
  • @RobAnthony I scrolled to the exact same position as it was in the first place and this is what I get, aslo tried to set it hardcoded where the calculation should happen and it works fine. – Rand Random Aug 14 '17 at 12:04
  • I can't make sense of the numbers you have given in the example. The connection between ScrollPercent, ScrollBarMaximum and ScrollBarValue is easy to work out it is ScrollPercent = ScrollBarValue / ScrollBarMaximum. This works for both sets of numbers. It also connects the 2.32 value to 1054 and 45279 but If you are 64% of the way through a document sized 12803 you would be 18% of the way through a document sized 45279, not 2.32% – Rob Anthony Aug 14 '17 at 14:03
  • @RobAnthony I believe you didn't take into account that when the ViewSize is relatively small and the scroll thumb is rather big that the scroll value isn't the top of the thumb I think it's the bottom. There for you are getting different results. I believe this can be seen when you compare the scroll bar max in comparison to the large change, the large change is way bigger than the scroll maximum. – Rand Random Aug 14 '17 at 17:00

1 Answers1

1

So, it looks like your scrollPercent is calculated like this:

scrollPercent = 100.0 * scrollBarValue / (scrollBarMaximum - 1);

So the math should work out like this (assuming floating-point division):

scrollPercentOld = 100.0 * scrollBarValueOld / (scrollBarMaximumOld - 1)
scrollPercentNew = 100.0 * scrollBarValueOld / (scrollBarMaximumNew - 1)

From that, you have:

scrollBarValueOld = scrollPercentOld * (scrollBarMaximumOld - 1) / 100.0
scrollBarValueOld = scrollPercentNew * (scrollBarMaximumNew - 1) / 100.0

When you equate the two, you get:

scrollPercentOld * (scrollBarMaximumOld - 1) = scrollPercentNew * (scrollBarMaximumNew - 1)

And then finally:

scrollPercentNew = scrollPercentOld * (scrollBarMaximumOld - 1) / (scrollBarMaximumNew - 1)
  • What would be the "total height"? Can I assume it is the ``scrollbarMaximum``? – Rand Random Aug 16 '17 at 09:19
  • I just edited my answer to use the terms & names from your question. Just did some calculations, looks like "total height" corresponds to `(scrollBarMaximum - 1)` – Filip Milovanović Aug 16 '17 at 09:26
  • That seems to be the same approach @RobAnthony used (see comments below question), since your calculation also results in ``18.34``. Which isnt correct, the new scrollPercent should be ``2.3278413357480452``. Thats what I used ``64,8648648648648*(12803-1)/(45279-1)`` – Rand Random Aug 16 '17 at 09:34
  • P.S. When calculated this way, `scrollPercentNew` should maintain the same `scrollBarValue` between the changes - but, perhaps I misunderstood you. Why 2.3278413357480452? – Filip Milovanović Aug 16 '17 at 09:35
  • Thats the new value I get when I ``manually`` move the position to the place where it was before, thats why. – Rand Random Aug 16 '17 at 09:38
  • Are you by any chance inserting or removing pages at a location that comes *before* the current scroll position? Because then what I proposed wouldn't work, and you would need to track these changes somehow. – Filip Milovanović Aug 16 '17 at 09:39
  • No, just after the scroll position. I have a new created word document (nothing fancy). I inserted 3 line breaks and wrote "Hello", put the cursor after the word. Than I scroll 4 times which results in the letters "Hello" almost not visible bascially just litte lines can be seen (maybe 1 millimeter of the letters). Than I call the method ``Application.Selection.InsertNewPage();`` 5 times. Word than moves the scroll to the last page (6th page) and I desire the scroll position unchanged, I there for manually scroll back where I was and again evaluate the values and thats what I get. – Rand Random Aug 16 '17 at 09:45
  • Hm... Are you sure you haven't mixed up values from two different tests? – Filip Milovanović Aug 16 '17 at 09:57
  • 100% sure. Would it help if I provide you my test document? – Rand Random Aug 16 '17 at 09:58
  • I'm asking because the `scrollBarValue = 1054` and `scrollPercent = 2.3278413357480452` give `scrollBarMaximum = 45278`, which is different from the `39795` in your test scenario. If you are 100% sure though, then either I made wrong assumptions about how this is calculated, or there's something funky going on with the code. – Filip Milovanović Aug 16 '17 at 10:05
  • ? but the ``scrollBarMaximum`` is ``45279`` the ``39795`` you are talking about is the ``scrollBarValue`` which is the incorrect value after inserting 5 pages. – Rand Random Aug 16 '17 at 11:09
  • I'm sorry I got the numbers mixed up. I have another idea. The way this should work is that the old value should be multiplied by a scaling factor. So `scrollPercentNew = scrollPercentOld * k`, where k is the unknown factor. For the example you give, this yields about `0.03588755`, but this only works for that particular scenario. The only way I found to reconstruct that number is to do this: `(scrollBarMaximumOld / scrollBarLargeChangeOld) / (scrollBarMaximumNew / scrollBarLargeChangeNew)`; with your test values, this gives approximately `0.03588732`, which is pretty close. – Filip Milovanović Aug 16 '17 at 12:34
  • Essentially, `scrollBarMaximum / scrollBarLargeChange` expresses the `scrollBarMaximum` in "units" of the large change, and my hope is that this will remain consistent after you change the number of pages. (Note: Make sure to test this with different numbers of inserted pages. Also, I'm writing in the comment section as I'm not certain this will work.) – Filip Milovanović Aug 16 '17 at 12:34
  • Its a tiny bit off when I insert 500 pages instead of 5, but looks promising. – Rand Random Aug 16 '17 at 16:16