简体   繁体   English

WPF,不透明度和线程

[英]WPF, Opacity and Threads

I have two overlapping (in same position, having the same size) MediaElements.我有两个重叠的(在同一个 position 中,具有相同的大小)MediaElements。 They will contain images.它们将包含图像。 The opacity of one element will be set to 1.0 and the opacity of the other set to 0.0.一个元素的不透明度将设置为 1.0,而另一个元素的不透明度将设置为 0.0。 The idea here would be a simple transition for a slide-show type deal.这里的想法是幻灯片类型交易的简单过渡。 When it's time to display the next slide, the background element loads a picture and the opacity of both elements switches gradually.当需要显示下一张幻灯片时,背景元素会加载一张图片,并且两个元素的不透明度会逐渐切换。

I tried (successfully) to implement this behavior using System.Timers, only to find that having more than some arbitrary number of timers in the same application would cause .NET to randomly spawn and cede control of timer_elapsed to several different threads.我尝试(成功地)使用 System.Timers 实现此行为,但发现在同一应用程序中拥有多个任意数量的计时器会导致 .NET 随机产生并将 timer_elapsed 的控制权交给几个不同的线程。 This caused unpredictable results and generally made me question my sanity.这导致了不可预测的结果,并且通常让我质疑自己的理智。

So, I decided to do the same thing, but with System.Threads and their Sleep functions.所以,我决定做同样的事情,但使用 System.Threads 和它们的 Sleep 函数。 For whatever reason, gradually cycling the opacity worked perfectly with the insane timers but fails utterly with threads.无论出于何种原因,逐渐循环不透明度与疯狂的计时器完美配合,但与线程完全失败。 And it fails in a ridiculous way.它以一种荒谬的方式失败了。 The opacity of both elements does change, but there's no in between.两个元素的不透明度确实发生了变化,但两者之间没有变化。 The element is shown either with opacity at 1.0 or 0.0.元素以 1.0 或 0.0 的不透明度显示。 Otherwise I would notice that roughly half the pictures weren't being cycled through.否则我会注意到大约一半的图片没有被循环播放。

After much googling, I thought perhaps the priority of the thread that the opacity changes were occurring on was somehow keeping the UI elements from being rendered immediately.经过大量的谷歌搜索,我认为可能发生不透明度更改的线程的优先级以某种方式阻止了 UI 元素立即呈现。 But then I recalled that because I was using dispatcher invocations on the media elements, all of the action was taking place on the main thread anyway, so it wouldn't make a difference.但后来我回忆起,因为我在媒体元素上使用了调度程序调用,所以所有的操作都发生在主线程上,所以不会有什么不同。

Contemplate the following code: https://gist.github.com/956093考虑以下代码: https://gist.github.com/956093

As suggested you should use the native animations;按照建议,您应该使用本机动画; i have come accross this thread issue before as well and in general i try to avoid using Dispatchers, i pretty much only use them to modify data if i must (eg ObservableCollection cannot be modified in a background thread, don't know any other examples actually).我以前也遇到过这个线程问题,一般来说,我尽量避免使用调度程序,我几乎只在必须时使用它们来修改数据(例如 ObservableCollection 不能在后台线程中修改,不知道任何其他示例实际上)。

You could still use normal threads, they work well if you use bindings to update the UIElements which nicely bypasses the dispatching issue.您仍然可以使用普通线程,如果您使用绑定来更新 UIElements,它们可以很好地绕过调度问题。

Animation example: Animation 示例:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM