Search code examples
c#wpfmultithreadinguser-interfacebackgroundworker

How to use BackgroundWorker to show/hide WPF UI element?


I have a rectangle which I want to make visible, then I want to wait for 500 milliseconds, then I want to make it invisible again.

Initially I tried this code:

MuzzleFlash.Visibility = Visibility.Visible;
Thread.Sleep(500);
this.UpdateLayout();
this.InvalidateVisual();
MuzzleFlash.Visibility = Visibility.Collapsed;

I tried those two intermediate lines as they supposedly forced windows to update, but all this did was pause for half a second without changing any sort of rectangles.

So I heard about BackgroundWorkers and how they're what I should use. After some brief reading, I came up with this. Note that Shoot is subscribed to a MouseDown event on a Canvas, which worked previously:

private void Shoot(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {

        BackgroundWorker UIUpdater = new BackgroundWorker();
        UIUpdater.WorkerSupportsCancellation = true;
        UIUpdater.WorkerReportsProgress = false;

        UIUpdater.DoWork += new DoWorkEventHandler(UI_DoWork);
        UIUpdater.RunWorkerCompleted += new RunWorkerCompletedEventHandler(cancelUIUpdate);

    }

    private void UI_DoWork(object sender, DoWorkEventArgs e)
    {
        MuzzleFlash.Visibility = Visibility.Visible;
        Thread.Sleep(500);
        this.UpdateLayout();
        this.InvalidateVisual();
        MuzzleFlash.Visibility = Visibility.Collapsed;
    }

    private void cancelUIUpdate(object sender, RunWorkerCompletedEventArgs e)
    {
        BackgroundWorker bw = sender as BackgroundWorker;
        bw.CancelAsync();
    }

Now it doesn't even pause for half a second, indicating to me that the worker isn't doing anything. How can I fix this, and make the rectangle appear/disappear?


Solution

  • It is bad idea to use background worker to manage animation in wpf. Animation should run from Storyboard. It can be specified through xaml or in code.

    Your question of animating a rectangle is very simple with xaml:

    <Rectangle x:Name="MuzzleFlash" Height="100" Width="100" Fill="Red">
        <Rectangle.Triggers>
            <EventTrigger RoutedEvent="Loaded">
                <BeginStoryboard>
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames BeginTime="0:0:0.5"  Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" />
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Rectangle.Triggers>
    </Rectangle>
    

    For more complex animations, you can use Blend to design them interactively.

    Updated response after comments: If you want to make appear and disappear, you have to manipulate it from a parent container, as once rectangle hidden, it won't respond to mouse events.

    You can put the rectangle in a grid and reference it by name to change its visibility in animation.

    <Grid Background="Gray">
        <Grid.Triggers>
            <EventTrigger RoutedEvent="MouseDown">
                <BeginStoryboard>
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames BeginTime="0:0:0.0"  Storyboard.TargetProperty="Visibility" Storyboard.TargetName="MuzzleFlash">
                            <DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" />
                            <DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="{x:Static Visibility.Hidden}" />
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Grid.Triggers>
        <Rectangle x:Name="MuzzleFlash" Height="100" Width="100" Fill="Red">
    
        </Rectangle>
    </Grid>