有没有一种方法可以延迟WPF数据绑定(提高渲染效果)?

6

这是我的问题。

ListBox A 显示 Observable Collection 中的所有项目。

ListBox B 仅显示在 ListBox A 中选择的项目。

    <ListBox ItemsSource="{Binding MyView}" Name="ListBoxA">
    <ListBox ItemsSource="{Binding Path=SelectedItems, 
        ElementName=ListBoxA}" Name="ListBoxB">

当ListBox A中的选择更改时,StoryBoard会运行。结果UI是基于用户选择的ListBox B的酷炫且平滑的滑入和滑出。
问题在于,当我的ListBox A仅数据绑定Name属性时,ListBox B会绑定几十甚至数百个属性。
问题还在于,在WPF中进行数据绑定时,渲染时会创建一个50-500毫秒的短暂UI延迟(特别是在动态时)。UI会冻结。
尽管可以容忍此问题,但我的StoryBoard似乎受到了这种DataBinding延迟的阻碍。结果,UI有点"突然"并停顿不前,我的平滑StoryBoard也无法展示。
我通过附加到StoryBoard.Completed事件来解决此问题。一旦StoryBoard完成,我就设置ListBox B的ItemsSource。
然而,这只有50%的好处。用户确实看到了StoryBoard的执行过程。但ListBox B的结果UI在动画后仍然 "突然"出现。
我认为正确的解决方法是在ListBox B内呈现的控件中某种方式指示等待,或延迟实际数据绑定。这将允许UI被渲染并参与StoryBoard,但数据稍后再 "填充"(希望也能延迟由DataBinding引起的延迟)。
有人遇到过类似的问题吗?
以下是演示问题的XAML(因为StackOverflow限制了问题的大小,您需要添加其他文本框才能真正看到延迟明显):
<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:sys="clr-namespace:System;assembly=mscorlib"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Page.Resources>

    <x:Array Type="{x:Type sys:String}" x:Key="MyData">
      <sys:String>One</sys:String>
      <sys:String>Two</sys:String>
      <sys:String>Three</sys:String>
      <sys:String>Four</sys:String>
      <sys:String>Five</sys:String>
      <sys:String>Six</sys:String>
    </x:Array>

    <Storyboard x:Key="MyGrowStoryboard">
        <ParallelTimeline>
            <DoubleAnimation To="1" DecelerationRatio="0.5" 
        Duration="00:00:00.500" 
        Storyboard.TargetName="MyTransform" 
        Storyboard.TargetProperty="ScaleX"  />
            <DoubleAnimation To="1" DecelerationRatio="0.5" 
        Duration="00:00:00.500" 
        Storyboard.TargetName="MyTransform" 
        Storyboard.TargetProperty="ScaleY"  />
        </ParallelTimeline>
    </Storyboard>
    <Storyboard x:Key="MyShrinkStoryboard">
        <ParallelTimeline>
            <DoubleAnimation To=".1" DecelerationRatio="0.5" 
        Duration="00:00:00.500" 
        Storyboard.TargetName="MyTransform" 
        Storyboard.TargetProperty="ScaleX"  />
            <DoubleAnimation To=".1" DecelerationRatio="0.5" 
        Duration="00:00:00.500" 
        Storyboard.TargetName="MyTransform" 
        Storyboard.TargetProperty="ScaleY"  />
        </ParallelTimeline>
    </Storyboard>

  </Page.Resources>
  <StackPanel>
    <ListBox ItemsSource="{Binding Source={StaticResource MyData}}" 
    Name="ListBoxA">
      <ListBox.ItemsPanel>
          <ItemsPanelTemplate>
              <StackPanel Orientation="Vertical" />
          </ItemsPanelTemplate>
      </ListBox.ItemsPanel>
      <ListBox.ItemTemplate>
        <DataTemplate>
          <DataTemplate.Triggers>

              <!-- grow -->
              <MultiDataTrigger>
                  <MultiDataTrigger.Conditions>
                      <Condition Value="True" 
            Binding="{Binding Path=IsSelected, 
            RelativeSource={RelativeSource 
            Mode=FindAncestor, 
            AncestorType={x:Type ListBoxItem}}}" />
                  </MultiDataTrigger.Conditions>
                  <MultiDataTrigger.EnterActions>
                      <BeginStoryboard 
            Storyboard="{StaticResource MyGrowStoryboard}" />
                  </MultiDataTrigger.EnterActions>
                  <MultiDataTrigger.ExitActions>
                      <BeginStoryboard 
            Storyboard="{StaticResource MyShrinkStoryboard}" />
                  </MultiDataTrigger.ExitActions>
              </MultiDataTrigger>

              <!-- shrink -->
              <MultiDataTrigger>
                  <MultiDataTrigger.Conditions>
                      <Condition Value="False" 
            Binding="{Binding Path=IsSelected, 
            RelativeSource={RelativeSource 
            Mode=FindAncestor, 
            AncestorType={x:Type ListBoxItem}}}" />
                      <Condition Value="1" 
            Binding="{Binding Path=SelectedItems.Count, 
            RelativeSource={RelativeSource 
            Mode=FindAncestor, 
            AncestorType={x:Type ListBox}}}" />
                  </MultiDataTrigger.Conditions>
                  <MultiDataTrigger.EnterActions>
                      <BeginStoryboard 
            Storyboard="{StaticResource MyShrinkStoryboard}" />
                  </MultiDataTrigger.EnterActions>
                  <MultiDataTrigger.ExitActions>
                      <BeginStoryboard 
            Storyboard="{StaticResource MyGrowStoryboard}" />
                  </MultiDataTrigger.ExitActions>
              </MultiDataTrigger>        

          </DataTemplate.Triggers>
          <TextBlock Text="{Binding .}">
            <TextBlock.LayoutTransform>
                <ScaleTransform ScaleX="1" ScaleY="1" 
            x:Name="MyTransform"/>
            </TextBlock.LayoutTransform>
          </TextBlock>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>

    <ListBox ItemsSource="{Binding Path=SelectedItems, 
        ElementName=ListBoxA}" Name="ListBoxB">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <UniformGrid Columns="10">
            <!-- repeat this part MANY times (like 3000) ! -->
            <TextBox Text="{Binding .}" />
            <TextBox Text="{Binding .}" />
            <TextBox Text="{Binding .}" />
            <TextBox Text="{Binding .}" />
            <TextBox Text="{Binding .}" />
          </UniformGrid>
       </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
  </StackPanel>
</Page>

看起来像这样:

在此输入图片描述

谢谢!
2个回答

4

在此之前我从未遇到过类似的问题,据我所知在WPF中没有内置的延迟数据绑定的方式。

我试过在ListBoxB中添加超过1000个文本框,并且ListBoxB渲染得非常快。但是如果你仍然想缩短ListBoxB的渲染时间,你可以尝试将以下代码添加到ListBoxB中。

VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"

这将仅生成ListBoxB中当前可见的控件,因此它将改善ListBoxB的渲染。

您可以查看我的文章VirtualizingStackPanel

希望这个答案能帮到你。


0

除了尽量减少生成控件中可见元素的数量外,没有其他处理方法。当然,你也可以买一台更大的电脑,但那只是开玩笑。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接