WPF、不透明度和线程

发布于 2024-11-05 03:47:31 字数 686 浏览 2 评论 0原文

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

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

因此,我决定做同样的事情,但使用 System.Threads 及其 Sleep 函数。不管出于什么原因,逐渐循环不透明度对于疯狂的计时器来说效果很好,但对于线程来说却完全失败了。它以一种荒谬的方式失败了。两个元素的不透明度确实发生了变化,但中间没有变化。显示的元素的不透明度为 1.0 或 0.0。否则我会注意到大约一半的图片没有被循环浏览。

经过大量谷歌搜索后,我认为可能发生不透明度更改的线程的优先级在某种程度上阻止了 UI 元素立即渲染。但后来我想起,因为我在媒体元素上使用调度程序调用,所以所有操作无论如何都发生在主线程上,因此不会产生任何影响。

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

I have two overlapping (in same position, having the same size) 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. 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. 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. 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. 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. 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

撩发小公举 2024-11-12 03:47:31

根据建议,您应该使用原生动画;我之前也遇到过这个线程问题,一般来说,我尽量避免使用调度程序,我几乎只在必须时使用它们来修改数据(例如 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();
}

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 (e.g. ObservableCollection cannot be modified in a background thread, don't know any other examples actually).

You could still use normal threads, they work well if you use bindings to update the UIElements which nicely bypasses the dispatching issue.

Animation example:

<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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文