WPF, Opacity and Threads
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 c开发者_开发技巧ode: 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 (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();
}
精彩评论