WPF 如何在 ScrollViewer 的虚拟化堆栈面板中调用所有可见容器的度量

本文关键字:调用 度量 ScrollViewer 虚拟化 堆栈 WPF | 更新日期: 2023-09-27 18:33:46

目标:重新测量,然后仅重绘那些在滚动查看器的虚拟化堆栈面板中查看的信号图。

当前版本:

目前,我有一个依赖属性(UnitsOfTimePerAxisDivision(,每当滚动查看器(信号图(中的容器更新时,它都会影响度量。

public static readonly DependencyProperty UnitsOfTimePerAxisDivisionProperty =
      DependencyProperty.Register("UnitsOfTimePerAxisDivision",
      typeof(int), typeof(SignalGraph),
      new FrameworkPropertyMetadata(1), FrameworkPropertyMetadataOptions.AffectsMeasure);

但是,这会更新每个信号图,包括那些不可见的信号图。 运行测量,然后重绘每个信号太耗时了。

  1. 如何仅更新可见的?

我想做一些这样的测试,

在 WPF 中,如何确定控件是否对用户可见?

通过基本上设置一个属性更改回调,我在其中测试每个信号是否可见。我最终仍然会测试所有信号,但至少我不会绘制所有信号。

我的第二个问题是是否有某种方法可以通过依赖在度量/排列中调用的函数来实现这一点。

我对虚拟化堆栈面板的理解如下:在顶层,我们有一个包含滚动查看器的树视图。ScrollViewer 的控件模板包含一个网格,ScrollContentPresenter 在其中将项放置在具有设置大小的列和行中。

        ControlTemplate TargetType="{x:Type ScrollViewer}">
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="*"/>
              <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
              <RowDefinition Height="*"/>
              <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <ScrollContentPresenter 
                Grid.Column="0"
                Grid.Row="0" />
            <ScrollBar 
                Margin="{Binding RelativeSource={RelativeSource AncestorType={x:Type DaedalusGraphViewer:GraphViewer}}, 
                        Path=GraphHeight, Converter={StaticResource ScrollBarTopMarginConverter}}"
                Grid.Row="0"
                Grid.Column="1"
                Width="18"
                Name="PART_VerticalScrollBar"
                Orientation="Vertical"
                Value="{TemplateBinding VerticalOffset}"
                Maximum="{TemplateBinding ScrollableHeight}"
                ViewportSize="{TemplateBinding ViewportHeight}"
                Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
            <ScrollBar 
                Height="18"
                Name="PART_HorizontalScrollBar"
                Orientation="Horizontal"
                Grid.Row="1"
                Grid.Column="0"
                Value="{TemplateBinding HorizontalOffset}"
                Maximum="{TemplateBinding ScrollableWidth}"
                ViewportSize="{TemplateBinding ViewportWidth}"
                Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
          </Grid>
        </ControlTemplate>

因此,让我们假设我告诉 Scrollviewer 重新测量,并且滚动查看器在屏幕上占用 100x100。这会导致滚动查看器通过调用 grids.measure(可用大小(来测量网格,可用大小为 100x100。

然后网格扣除滚动条的大小(我们只猜测十个(,并告诉 scrollviewerpresenter 用 90x90 测量自己。演示者使用虚拟化堆栈面板,然后为孩子们提供无限的大小来衡量自己(我认为是垂直和水平(。 但是,虚拟化堆栈面板仅测量子级(信号图(,直到它填满其 90x90 的大小,然后排列阶段从滚动查看器向下进入虚拟化堆栈面板。

如果是这种情况,我可以在信号图的排列方法结束时拨打电话,并且只应排列和重新绘制屏幕上的信号图。

  protected override Size ArrangeOverride(Size finalSize)
    {
      visuals.Clear();
      DrawSignal();
      return base.ArrangeOverride(finalSize);
    }

但是,我发现当我为滚动查看器和信号图在度量覆盖中设置断点时,信号图中的断点没有命中。

我不明白如果重新测量滚动查看器,为什么不重新测量信号图。

    度量
  1. 值函数是否存储其父级传递给它的最后一个大小约束 (availableSize(,然后除非约束更改,否则不会执行新度量值?我认为可能导致无法测量的另一种可能性是,如果滚动查看器独立于其子级的大小计算其大小,然后仅在大小是新大小时才测量它们。

    但也许问题是这样的。 如果有人有反射器并且可以为我提供实际的 ScrollViewer 测量覆盖代码,那就太神奇了。如果没有,我明天可能会尝试获得反射器并弄清楚如何使用它。

    覆盖

    度量覆盖 ( 大小 可用大小({ 尺寸新大小 = 可用大小。 如果(可用大小!=这个。所需大小( { 测量辣椒((; }}

  2. 如果不是这种情况,那么是什么原因导致信号图无法测量?

编辑:

是的,我有一些东西要虚拟化。我不是完全疯了。只是大部分都是疯狂的。应用此样式似乎会破坏虚拟化。删除它使一切正常。

使用内置的虚拟化比尝试实际测试每个现有信号图的可见性要好得多。 我仍然不知道上面#2的答案,但是如果我可以让这种虚拟化工作,我将不需要它。但是,我想保持我目前的风格,并弄清楚为什么这种风格正在破坏虚拟化。

<!--Style for scrollviewer for signals-->
  <Style x:Key="SignalScrollViewerStyle" TargetType="{x:Type Components:SignalScrollViewer}">
    <Setter Property="OverridesDefaultStyle" Value="True"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type ScrollViewer}">
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="*"/>
              <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
              <RowDefinition Height="*"/>
              <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <ScrollContentPresenter 
                Grid.Column="0"
                Grid.Row="0" />
            <ScrollBar 
                Margin="{Binding RelativeSource={RelativeSource AncestorType={x:Type DaedalusGraphViewer:GraphViewer}}, 
                        Path=GraphHeight, Converter={StaticResource ScrollBarTopMarginConverter}}"
                Grid.Row="0"
                Grid.Column="1"
                Width="18"
                Name="PART_VerticalScrollBar"
                Orientation="Vertical"
                Value="{TemplateBinding VerticalOffset}"
                Maximum="{TemplateBinding ScrollableHeight}"
                ViewportSize="{TemplateBinding ViewportHeight}"
                Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
            <ScrollBar 
                Height="18"
                Name="PART_HorizontalScrollBar"
                Orientation="Horizontal"
                Grid.Row="1"
                Grid.Column="0"
                Value="{TemplateBinding HorizontalOffset}"
                Maximum="{TemplateBinding ScrollableWidth}"
                ViewportSize="{TemplateBinding ViewportWidth}"
                Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
          </Grid>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

编辑2:

在此页面上找到了解决方案:我在此页面上找到了解决方案:

"http://social.msdn.microsoft.com/Forums/vstudio/en-US/deae32d8-d7e2-4737-8c05-bbd860289425/lost-virtualization-on-styled-scrollviewer?forum=wpf

覆盖默认的 ScrollViewer 样式时,我没有将附加属性绑定到 ScrollViewer 内容演示器。 呃,令人惊讶的是,那条线很难找到。我快疯了,以为我不知道虚拟化到底应该做什么。

现在唯一麻烦的是,我仍然不完全了解布局过程的工作原理。如果我有一个具有metadaoption影响度量的依赖属性,然后依赖项属性更改,则该项目将重新测量。如果元素的大小发生变化,父母会重新测量吗?

这是我的猜测,但它仍然不能解释为什么我的滚动查看器子项在滚动查看器的测量周期中没有重新测量。

WPF 如何在 ScrollViewer 的虚拟化堆栈面板中调用所有可见容器的度量

使用反射器是没有意义的。VirtualizingStackPanel 维护一个可见容器列表。当您开始向上或向下滚动时,VirtualizingStackPanel 将删除不再需要的容器,但会创建一个新容器。最后最终删除了 5 个旧项目,但添加了 5 个新项目,因此可见容器的计数始终保持不变,因此只需更改属性 UnitsOfTimePerAxisDivision,而不必担心哪些容器可见。

如果 ScrollViewer 将 90x90 传递给 VirtualizingStackPanel,并且 VirtualizingStackPanel 将无限传递到容器,则只有在处于无效度量状态时,容器才会被重新测量,否则它们会返回"旧"大小,即它们当前的大小。

只需让 WPF 完成工作,无需担心性能问题。VirtualizingStackPanel 包含可见容器,因此不会对所有可能的容器进行任何重新测量或性能影响。即使您有 1000 个项目,也只会使用 20 个。

所以问题是我未能以我的滚动查看器样式将 cancontentscroll 属性附加到我的滚动内容演示器。

 <ScrollContentPresenter 
                CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}"