为什么没有设置滚动所有者

本文关键字:滚动 所有者 设置 为什么 | 更新日期: 2023-09-27 17:51:05

我开发测试驱动并想要测试,我的控件实现IScrollInfo返回正确的ScrollViewer。此外,我想测试我的控件是否在 ScrollOwner 上调用了 InvalidateScrollInfo

控件如下所示:

private class MyControl : UserControl, IScrollInfo
{
    public ScrollViewer ScrollOwner { get; set; }
    ... other code to implement IScrollInfo
}

我的测试(nunit(如下所示:

[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));
}

但不幸的是,Assert.That(ControlToTest.ScrollOwner, Is.SameAs(scrollViewer));失败了,因为ScrollOwner null

问题 为什么ControlToTest.ScrollOwner为空?

做了什么:我查看了ScrollOwner的设置位置。这发生在ScrollContentPresenterHookupScrollingComponents中,而OnApplyTemplate又被称为。据我了解,模板是在测量元素时应用的。这应该在显示窗口时完成。即使我window.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.Render);添加到表单中:

[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));
}

它没有帮助。ScrollOwner仍然null.为什么?

奖励问题:如何测试InvalidateScrollInfo()是否在scrollViewer上被调用?

为什么没有设置滚动所有者

必须将

CanContentScroll设置为 true

比测试有效:

[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));
}

现在可以使用ControlToTest.ScrollOwner进行进一步的测试。

奖励问题的答案:当您将ControlToTest.ViewportHeight更改为小于测试中的ControlToTest.ExtentHeight时,您可以检查 ControlToTest.ScrollOwner.ComputedVerticalScrollBarVisibility 属性以断言调用了ScrollOwner.InvalidateScrollInfo()。这可能如下所示:

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));

private void Render()
{
    ControlToTest.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);
}
测试 UI

很棘手,WPF 中发生的情况是,操作排队到调度程序上,并且仅在 UI 线程空闲后执行。所以很多窗口后的初始化。Show(( 不会发生,直到您释放 UI 的线程以执行其绑定/初始化,并将任务调度到优先级非常低的调度程序上,该调度程序将执行断言。这在单元测试中可能非常棘手。