如何在WPF中制作流畅的幻灯片(高分辨率图像)

时间:2013-11-18 11:58:09

标签: c# wpf storyboard translation

我使用WPF及其故事板制作了幻灯片。 创建幻灯片放映的最大努力是高分辨率图像。 每张图片为8850x1920像素。该应用程序旨在用于包含4x2显示器和Matrox显卡的大型视频墙屏幕,从而制作一个大屏幕。

问题在于如此大的屏幕图像正在跳过和滑动。我想这是因为WPF的大图像和不可能与Realtime Timers一起工作。 我想一切都在运行时没有最佳分辨率的Dispatcher计时器。

我的问题是:有没有办法让它更快,而不是跳过和滑倒?

我可以达到某种更好的质量,增加转换总时间,但在大显示器上,每个细节都可见。

   <Storyboard  x:Key="sbHorizontalSlideTransition"
                FillBehavior="HoldEnd">
        <!--SCROLL TO THE MULTIPLIER OF THE WIDTH-->
        <DoubleAnimationUsingKeyFrames 
                            Storyboard.TargetName="Mediator"
                            Storyboard.TargetProperty="ScrollableWidthMultiplier">
            <LinearDoubleKeyFrame KeyTime="00:0:0" Value="0" />
            <LinearDoubleKeyFrame KeyTime="00:15:00" Value="1" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>

...

  <Grid x:Name="gdContainer" Background="Black">
        <ScrollViewer x:Name="Scroller"  
                      HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                      Background="Black"
                      VerticalScrollBarVisibility="Visible" 
                      HorizontalScrollBarVisibility="Visible"
                      ScrollChanged="Scroller_ScrollChanged">
            <StackPanel x:Name="ScrollPanel1" Orientation="Horizontal">
                <Image Height="{Binding Path=ActualHeight, ElementName=gdContainer}" Width="Auto" Stretch="Uniform"
                    Source="/Images/img1-01.jpg"/>
                <Image Height="{Binding Path=ActualHeight, ElementName=gdContainer}" Width="Auto" Stretch="Uniform"
                    Source="/Images/img2-01.jpg"/>
                <Image Height="{Binding Path=ActualHeight, ElementName=gdContainer}" Width="Auto" Stretch="Uniform"
                    Source="/Images/img3-01.jpg"/>
                <Image Height="{Binding Path=ActualHeight, ElementName=gdContainer}" Width="Auto" Stretch="Uniform"
                    Source="/Images/img4-01.jpg"/>
            </StackPanel>
        </ScrollViewer>


        <!-- Mediator that forwards the property changes -->
        <cmn:ScrollViewerOffsetMediator x:Name="Mediator" ScrollViewer="{Binding ElementName=Scroller}"/>


    </Grid>

...

/// <summary>
/// Mediator forwards Offset property changes on to a ScrollViewer
/// instance to enable the animation of Horizontal/VerticalOffset.
/// </summary>

public class ScrollViewerOffsetMediator : FrameworkElement
{
    /// <summary>
    /// ScrollViewer instance to forward Offset changes on to.
    /// </summary>
    public ScrollViewer ScrollViewer
    {
        get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
        set { SetValue(ScrollViewerProperty, value); }
    }
    public static readonly DependencyProperty ScrollViewerProperty =
        DependencyProperty.Register(
            "ScrollViewer",
            typeof(ScrollViewer),
            typeof(ScrollViewerOffsetMediator),
            new PropertyMetadata(OnScrollViewerChanged));
    private static void OnScrollViewerChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var mediator = (ScrollViewerOffsetMediator)o;
        var scrollViewer = (ScrollViewer)(e.NewValue);
        if (null != scrollViewer)
        {
            scrollViewer.ScrollToVerticalOffset(mediator.VerticalOffset);
        }
    }

    /// <summary>
    /// VerticalOffset property to forward to the ScrollViewer.
    /// </summary>
    public double VerticalOffset
    {
        get { return (double)GetValue(VerticalOffsetProperty); }
        set { SetValue(VerticalOffsetProperty, value); }
    }
    public static readonly DependencyProperty VerticalOffsetProperty =
        DependencyProperty.Register(
            "VerticalOffset",
            typeof(double),
            typeof(ScrollViewerOffsetMediator),
            new PropertyMetadata(OnVerticalOffsetChanged));
    public static void OnVerticalOffsetChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var mediator = (ScrollViewerOffsetMediator)o;
        if (null != mediator.ScrollViewer)
        {
            mediator.ScrollViewer.ScrollToVerticalOffset((double)(e.NewValue));
        }
    }

    /// <summary>
    /// HorizontalOffset property to forward to the ScrollViewer.
    /// </summary>
    public double HorizontalOffset
    {
        get { return (double)GetValue(HorizontalOffsetProperty); }
        set { SetValue(VerticalOffsetProperty, value); }
    }
    public static readonly DependencyProperty HorizontalOffsetProperty =
        DependencyProperty.Register(
            "HorizontalOffset",
            typeof(double),
            typeof(ScrollViewerOffsetMediator),
            new PropertyMetadata(OnHorizontalOffsetChanged));
    public static void OnHorizontalOffsetChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var mediator = (ScrollViewerOffsetMediator)o;
        if (null != mediator.ScrollViewer)
        {
            mediator.ScrollViewer.ScrollToHorizontalOffset((double)(e.NewValue));
        }
    }

    /// <summary>
    /// Multiplier for ScrollableHeight property to forward to the ScrollViewer.
    /// </summary>
    /// <remarks>
    /// 0.0 means "scrolled to top"; 1.0 means "scrolled to bottom".
    /// </remarks>
    public double ScrollableHeightMultiplier
    {
        get { return (double)GetValue(ScrollableHeightMultiplierProperty); }
        set { SetValue(ScrollableHeightMultiplierProperty, value); }
    }
    public static readonly DependencyProperty ScrollableHeightMultiplierProperty =
        DependencyProperty.Register(
            "ScrollableHeightMultiplier",
            typeof(double),
            typeof(ScrollViewerOffsetMediator),
            new PropertyMetadata(OnScrollableHeightMultiplierChanged));
    public static void OnScrollableHeightMultiplierChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var mediator = (ScrollViewerOffsetMediator)o;
        var scrollViewer = mediator.ScrollViewer;
        if (null != scrollViewer)
        {
            scrollViewer.ScrollToVerticalOffset((double)(e.NewValue) * scrollViewer.ScrollableHeight);
        }
    }


    /// <summary>
    /// Multiplier for ScrollableWidth property to forward to the ScrollViewer.
    /// </summary>
    /// <remarks>
    /// 0.0 means "scrolled to top"; 1.0 means "scrolled to bottom".
    /// </remarks>
    public double ScrollableWidthMultiplier
    {
        get { return (double)GetValue(ScrollableWidthMultiplierProperty); }
        set { SetValue(ScrollableWidthMultiplierProperty, value); }
    }
    public static readonly DependencyProperty ScrollableWidthMultiplierProperty =
        DependencyProperty.Register(
            "ScrollableWidthMultiplier",
            typeof(double),
            typeof(ScrollViewerOffsetMediator),
            new PropertyMetadata(OnScrollableWidthMultiplierChanged));
    public static void OnScrollableWidthMultiplierChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var mediator = (ScrollViewerOffsetMediator)o;
        var scrollViewer = mediator.ScrollViewer;
        if (null != scrollViewer)
        {
            scrollViewer.ScrollToHorizontalOffset((double)(e.NewValue) * scrollViewer.ScrollableWidth);
        }
    }    
}

1 个答案:

答案 0 :(得分:0)

在速度和响应性方面最好的解决方案是使用翻译变换进行滚动并手动管理图像的无限旋转。当图像从屏幕出来时,它必须在列表的后面移动,改变Canvas.Left和Canvas.Top参数。附加的故事板将继续根据其原始参数进行滚动。

看起来故事板在一个比其他任何东西都要优先的线程中运行。