Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is ScrollOwner not set?

Tags:

c#

wpf

I develop test driven and want to test, that my control which implements IScrollInfo returns the correct ScrollViewer. Additionally I'd like to test that my control called InvalidateScrollInfo on the ScrollOwner.

The control looks like:

private class MyControl : UserControl, IScrollInfo
{
    public ScrollViewer ScrollOwner { get; set; }

    ... other code to implement IScrollInfo
}

My test (nunit) looks like:

[Test]
public void Should_be_scrollable()
{
    var ControlToTest = new MyControl();
    var scrollViewer = new ScrollViewer { Content = ControlToTest };
    var window = new Window { Content = scrollViewer };
    window.Show();

    Assert.That(ControlToTest, Is.InstanceOf<IScrollInfo>());
    Assert.That(ControlToTest.ScrollOwner, Is.SameAs(scrollViewer));
}

But unfortunately the Assert.That(ControlToTest.ScrollOwner, Is.SameAs(scrollViewer)); fails because the ScrollOwner is null.

Question Why is ControlToTest.ScrollOwner null?

What I did: I looked where the ScrollOwner gets set. This happens in the HookupScrollingComponents in the ScrollContentPresenter, which in turn is called in OnApplyTemplate. And for my understanding the template is applied when the element gets measured. And this should be done when the window is shown. Even when I added window.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.Render); to form:

[Test]
public void Should_be_scrollable()
{
    var scrollViewer = new ScrollViewer { Content = ControlToTest };
    var window = new Window { Content = scrollViewer };
    window.Show();
    window.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.Render);

    Assert.That(ControlToTest, Is.InstanceOf<IScrollInfo>());
    Assert.That(ControlToTest.ScrollOwner, Is.SameAs(scrollViewer));
}

it didn't help. The ScrollOwner is still null. Why?

Bonus question: How do I test that InvalidateScrollInfo() was called on the scrollViewer?

like image 468
Rico-E Avatar asked Dec 07 '25 02:12

Rico-E


1 Answers

It is neccessary, that the CanContentScroll is set to true.

Than the test works:

[Test]
public void Should_be_scrollable()
{
    var ControlToTest = new MyControl();
    var scrollViewer = new ScrollViewer
    {
        Content = ControlToTest,
        CanContentScroll = true
    };
    var window = new Window { Content = scrollViewer };
    window.Show();

    Assert.That(ControlToTest, Is.InstanceOf<IScrollInfo>());
    Assert.That(ControlToTest.ScrollOwner, Is.SameAs(scrollViewer));
}

Now further tests with the ControlToTest.ScrollOwner can be performed.

Answer to Bonus question: when you change the ControlToTest.ViewportHeight to be smaller than the ControlToTest.ExtentHeight in the test, then you can check the ControlToTest.ScrollOwner.ComputedVerticalScrollBarVisibility property to assert that the ScrollOwner.InvalidateScrollInfo() was called. This might look like:

var scrollViewer = new ScrollViewer
{
    Content = ControlToTest,
    CanContentScroll = true,
    VerticalScrollBarVisibility = ScrollBarVisibility.Auto
};
var window = new Window { Content = scrollViewer, Height = 600 };
window.Show();
Assert.That(
    ControlToTest.ScrollOwner.ComputedVerticalScrollBarVisibility,
    Is.EqualTo(Visibility.Collapsed));

window.Height = 300;
Render();

Assert.That(
    ControlToTest.ScrollOwner.ComputedVerticalScrollBarVisibility,
    Is.EqualTo(Visibility.Visible));

with

private void Render()
{
    ControlToTest.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);
}
like image 135
Rico-E Avatar answered Dec 08 '25 14:12

Rico-E