WPF,不透明度和线程

时间:2011-05-04 21:48:02

标签: c# wpf multithreading opacity

我有两个重叠(在相同的位置,具有相同的大小)MediaElements。它们将包含图像。一个元素的不透明度将设置为1.0,另一个元素的不透明度将设置为0.0。这里的想法是幻灯片式交易的简单过渡。当显示下一张幻灯片时,背景元素会加载图片,两个元素的不透明度会逐渐切换。

我尝试(成功)使用System.Timers实现此行为,但却发现在同一个应用程序中拥有超过一些任意数量的计时器会导致.NET随机生成并将timer_elapsed的控制权交给几个不同的线程。这导致了不可预测的结果,并且总体上让我质疑我的理智。

所以,我决定做同样的事情,但使用System.Threads和他们的Sleep功能。无论出于何种原因,逐渐循环不透明度与疯狂的计时器完美配合,但完全失败了。它以一种荒谬的方式失败了。两个元素的不透明度会发生变化,但两者之间没有变化。该元素的不透明度为1.0或0.0。否则我会注意到大约一半的照片没有被循环播放。

经过大量的谷歌搜索后,我想也许不透明度发生变化的线程的优先级是以某种方式保持UI元素不被立即呈现。但后来我回忆说,因为我在媒体元素上使用了调度程序调用,所以所有操作都在主线程上进行,所以它不会产生任何影响。

考虑以下代码:https://gist.github.com/956093

1 个答案:

答案 0 :(得分:0)

根据建议你应该使用原生动画;我之前也遇到了这个线程问题,一般来说我试图避免使用Dispatchers,我几乎只使用它们来修改数据,如果我必须的话(例如ObservableCollection不能在后台线程中修改,不知道任何其他的例子实际上)。

您仍然可以使用普通线程,如果您使用绑定来更新UIElements,它可以很好地绕过调度问题。

动画示例:

<Grid Name="testGrid" Tag="2">
    <Grid.Resources>
        <Storyboard x:Key="FadeAnim2to1">
            <DoubleAnimation Storyboard.Target="{x:Reference img1}"
                             Storyboard.TargetProperty="Opacity"
                             Duration="0:0:1" To="1"/>
            <DoubleAnimation Storyboard.Target="{x:Reference img2}"
                             Storyboard.TargetProperty="Opacity"
                             Duration="0:0:1" To="0"/>
        </Storyboard>
        <Storyboard x:Key="FadeAnim1to2">
            <DoubleAnimation Storyboard.Target="{x:Reference img1}"
                             Storyboard.TargetProperty="Opacity"
                             Duration="0:0:1" To="0"/>
            <DoubleAnimation Storyboard.Target="{x:Reference img2}"
                             Storyboard.TargetProperty="Opacity"
                             Duration="0:0:1" To="1"/>
        </Storyboard>
    </Grid.Resources>
    <Image x:Name="img1" Source="Images/Default.ico" Width="200" Height="200" Opacity="0"/>
    <Image x:Name="img2" Source="Images/Error.ico" Width="200" Height="200"/>
</Grid>
<Button Content="Fade" Click="Button1_Click"/>
private void Button1_Click(object sender, RoutedEventArgs e)
{
    Storyboard anim;
    if ((string)testGrid.Tag == "1") //This is just for brevity, you should of course not use the Tag to store state information, let alone number strings
    {
        anim = testGrid.Resources["FadeAnim1to2"] as Storyboard;
        testGrid.Tag = "2";
    }
    else
    {
        anim = testGrid.Resources["FadeAnim2to1"] as Storyboard;
        testGrid.Tag = "1";
    }
    anim.Begin();
}