0

I created a spinner control, like one below, but when using in a windows opened with win.Show() command, the animation does not work.

My code to open this windows is:

win = new WaitWindow("wait...");
win.Show();

//do some long processing

win.Close();

It looks like the window thread gets frozen.

What I can do?

This is the spinner control:

<UserControl.Resources>
<Color x:Key="FilledColor" A="255" B="155" R="155" G="155"/>
<Color x:Key="UnfilledColor" A="0" B="155" R="155" G="155"/>

<Style x:Key="BusyAnimationStyle" TargetType="Control">
    <Setter Property="Background" Value="#7F000000"/>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Control">
                <ControlTemplate.Resources>
                    <Storyboard x:Key="Animation0" BeginTime="00:00:00.0" RepeatBehavior="Forever">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse0" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                            <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>

                    <Storyboard x:Key="Animation1" BeginTime="00:00:00.2" RepeatBehavior="Forever">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse1" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                            <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>

                    <Storyboard x:Key="Animation2" BeginTime="00:00:00.4" RepeatBehavior="Forever">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse2" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                            <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>

                    <Storyboard x:Key="Animation3" BeginTime="00:00:00.6" RepeatBehavior="Forever">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse3" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                            <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>

                    <Storyboard x:Key="Animation4" BeginTime="00:00:00.8" RepeatBehavior="Forever">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse4" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                            <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>

                    <Storyboard x:Key="Animation5" BeginTime="00:00:01.0" RepeatBehavior="Forever">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse5" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                            <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>

                    <Storyboard x:Key="Animation6" BeginTime="00:00:01.2" RepeatBehavior="Forever">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse6" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                            <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>

                    <Storyboard x:Key="Animation7" BeginTime="00:00:01.4" RepeatBehavior="Forever">
                        <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse7" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                            <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                </ControlTemplate.Resources>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsVisible" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard Storyboard="{StaticResource Animation0}" x:Name="Storyboard0" />
                            <BeginStoryboard Storyboard="{StaticResource Animation1}" x:Name="Storyboard1"/>
                            <BeginStoryboard Storyboard="{StaticResource Animation2}" x:Name="Storyboard2"/>
                            <BeginStoryboard Storyboard="{StaticResource Animation3}" x:Name="Storyboard3"/>
                            <BeginStoryboard Storyboard="{StaticResource Animation4}" x:Name="Storyboard4"/>
                            <BeginStoryboard Storyboard="{StaticResource Animation5}" x:Name="Storyboard5"/>
                            <BeginStoryboard Storyboard="{StaticResource Animation6}" x:Name="Storyboard6"/>
                            <BeginStoryboard Storyboard="{StaticResource Animation7}" x:Name="Storyboard7"/>
                        </Trigger.EnterActions>

                        <Trigger.ExitActions>
                            <StopStoryboard BeginStoryboardName="Storyboard0"/>
                            <StopStoryboard BeginStoryboardName="Storyboard1"/>
                            <StopStoryboard BeginStoryboardName="Storyboard2"/>
                            <StopStoryboard BeginStoryboardName="Storyboard3"/>
                            <StopStoryboard BeginStoryboardName="Storyboard4"/>
                            <StopStoryboard BeginStoryboardName="Storyboard5"/>
                            <StopStoryboard BeginStoryboardName="Storyboard6"/>
                            <StopStoryboard BeginStoryboardName="Storyboard7"/>
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>

                <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
                    <Grid>
                    <Canvas Height="60" Width="60">
                        <Canvas.Resources>
                            <Style TargetType="Ellipse">
                                <Setter Property="Width" Value="15"/>
                                <Setter Property="Height" Value="15" />
                                <Setter Property="Fill" Value="#009B9B9B" />
                            </Style>
                        </Canvas.Resources>

                        <Ellipse x:Name="ellipse0" Canvas.Left="1.75" Canvas.Top="21"/>
                        <Ellipse x:Name="ellipse1" Canvas.Top="7" Canvas.Left="6.5"/>
                        <Ellipse x:Name="ellipse2" Canvas.Left="20.5" Canvas.Top="0.75"/>
                        <Ellipse x:Name="ellipse3" Canvas.Left="34.75" Canvas.Top="6.75"/>
                        <Ellipse x:Name="ellipse4" Canvas.Left="40.5" Canvas.Top="20.75" />
                        <Ellipse x:Name="ellipse5" Canvas.Left="34.75" Canvas.Top="34.5"/>
                        <Ellipse x:Name="ellipse6" Canvas.Left="20.75" Canvas.Top="39.75"/>
                        <Ellipse x:Name="ellipse7" Canvas.Top="34.25" Canvas.Left="7" />
                        <Ellipse Width="39.5" Height="39.5" Canvas.Left="8.75" Canvas.Top="8" Visibility="Hidden"/>
                    </Canvas>
                        <Label Content="{Binding Path=Text}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
</UserControl.Resources>
<Control Style="{StaticResource BusyAnimationStyle}"/>

update

my windows control looks like: Note: using win.ShowDialog(), works, but the remaining process not run while windows is open.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <local:Spinner Grid.Column="0" Width="16" Height="16" Margin="8,4,4,4" />
    <TextBlock Grid.Column="1" Name="MessageControl" VerticalAlignment="Center" Margin="8" />
</Grid>
Beetlejuice
  • 4,292
  • 10
  • 58
  • 84
  • What do you get by debugging it?no errors? – Ferus7 Dec 14 '17 at 12:29
  • No erros, only a frozen animation. With .ShowDialog() the animation works but must close window to continue – Beetlejuice Dec 14 '17 at 12:41
  • 2
    If you *`//do some long processing`* in the UI thread, this is sort of expected behavior. Load your long processing into a worker thread and notify the window to close after the processing completed. – grek40 Dec 14 '17 at 12:43
  • It sounds very much like you are expecting the UI thread to continue updating while you are running other code on the same thread. In order for your scenario to work, you need to run your `//do some long processing` code on a separate thread, like a `BackgroundWorker`. – Stewbob Dec 14 '17 at 12:43

3 Answers3

2

You may use Task.Run to start the long running action. However, instead of closing the Window from a Dispatcher action inside the Task action, you may just await the Task (from within a method that is marked as async):

Assuming there is some event handler that starts the processing:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    var win = new WaitWindow("wait...");
    win.Show();

    await Task.Run(() =>
    {
       // do some long processing
    });

    win.Close();
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
1

This is because you doing long action in the UI thread, use another thread instead, here is an example:

var win = new WaitWindow();
Task.Factory.StartNew(() =>
{
    try
    {
        //do some long processing
        Thread.Sleep(1000);
    }
    finally
    {
        win.Dispatcher.Invoke(() => { win.Close(); });
    }
});
win.Show();
Nenad
  • 316
  • 2
  • 14
Netstep
  • 492
  • 3
  • 12
  • See [here](https://stackoverflow.com/q/38423472/1136211) why `Task.Run` is recommended (instead of `Task.Factory.StartNew`). – Clemens Dec 14 '17 at 12:59
  • Yes, Task.Run also could be used, if your target is .net 4.5 or above – Netstep Dec 14 '17 at 13:03
0

You can use Task.Run and ContinueWith to load your work into a task and close the window when the task ended (note I don't check for task-success here).

var win = new WaitWindow("wait...");
win.Show();

Task.Run(() =>
{
    //do some long processing
    ProcessSomething();
}).ContinueWith(t =>
{
    Dispatcher.Invoke(() => win.Close());
});
grek40
  • 13,113
  • 1
  • 24
  • 50
  • This gives me a compilation error: An object reference is required for the non-static field, method, or property 'Dispatcher.Invoke(Action)' – Beetlejuice Dec 14 '17 at 13:20
  • @Beetlejuice you can use `Application.Current.Dispatcher` or similar. The Dispatcher is a standard technique in the context of WPF and depending on the placement of your code, a local dispatcher could be available (or not). – grek40 Dec 14 '17 at 14:11