Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding "Loading"-image on top of ListView

I'd like to add a "Loading"-Image on the top of my ListView while all items are loading, if there are a lot of items being loaded, I still want a nice user experience.

So therefore I want a floating Image ( Animated GIF ? ) on top of my ListView.

How would one solve this and once you have the floating image or control, how do you make the GIF animated?

like image 942
Filip Ekberg Avatar asked Nov 28 '22 20:11

Filip Ekberg


1 Answers

Here's some XAML that I use to create an AJAX-like wait spinner for WPF. I uses geometry and animation rather than an animated GIF, and you can control the size and rate by tweaking the XAML:

<!-- Style for AJAX-like wait spinners -->
<Style x:Key="WaitSpinnerStyle" TargetType="Control">
    <Setter Property="Foreground" Value="#888" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Control">
                <Viewbox Visibility="{TemplateBinding Visibility}">
                    <Canvas RenderTransformOrigin="0.5,0.5" Width="120" Height="120" >
                        <Ellipse Width="21.835" Height="21.862" Canvas.Left="20.1696" Canvas.Top="9.76358" Stretch="Fill" Fill="{TemplateBinding Foreground}" Opacity="1.0"/>
                        <Ellipse Width="20.835" Height="20.862" Canvas.Left="2.86816" Canvas.Top="29.9581" Stretch="Fill" Fill="{TemplateBinding Foreground}" Opacity="0.9"/>
                        <Ellipse Width="19.835" Height="19.862" Canvas.Left="0.00001" Canvas.Top="57.9341" Stretch="Fill" Fill="{TemplateBinding Foreground}" Opacity="0.8"/>
                        <Ellipse Width="17.835" Height="17.862" Canvas.Left="12.1203" Canvas.Top="83.3163" Stretch="Fill" Fill="{TemplateBinding Foreground}" Opacity="0.7"/>
                        <Ellipse Width="16.835" Height="16.862" Canvas.Left="36.5459" Canvas.Top="98.1380" Stretch="Fill" Fill="{TemplateBinding Foreground}" Opacity="0.6"/>
                        <Ellipse Width="14.835" Height="14.862" Canvas.Left="64.6723" Canvas.Top="96.8411" Stretch="Fill" Fill="{TemplateBinding Foreground}" Opacity="0.5"/>
                        <Ellipse Width="13.835" Height="13.862" Canvas.Left="87.6176" Canvas.Top="81.2783" Stretch="Fill" Fill="{TemplateBinding Foreground}" Opacity="0.4"/>
                        <Ellipse Width="12.835" Height="12.862" Canvas.Left="98.165"  Canvas.Top="54.4140" Stretch="Fill" Fill="{TemplateBinding Foreground}" Opacity="0.3"/>
                        <Ellipse Width="11.835" Height="11.862" Canvas.Left="92.9838" Canvas.Top="26.9938" Stretch="Fill" Fill="{TemplateBinding Foreground}" Opacity="0.2"/>
                        <Canvas.RenderTransform>
                            <RotateTransform x:Name="SpinnerRotate" Angle="0" />
                        </Canvas.RenderTransform>
                        <Canvas.Triggers>
                            <EventTrigger RoutedEvent="ContentControl.Loaded">
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation
                                            Storyboard.TargetName="SpinnerRotate"
                                            Storyboard.TargetProperty="Angle"
                                            From="0" To="360" Duration="0:0:01.3"
                                            RepeatBehavior="Forever" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Canvas.Triggers>
                    </Canvas>
                </Viewbox>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

You use it like this (specify the colour if you want to change it):

<Control Style="{StaticResource WaitSpinnerStyle}" Width="35" />
<Control Style="{StaticResource WaitSpinnerStyle}" Width="35" Foreground="Green" />

The above XAML would look like this (you have to imagine them spinning!):


To have a layer appear above your ListBox, wrap it in a Grid like this:

<Grid>
    <!-- LOADING overlay (for async-load) -->
    <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsHitTestVisible="True" 
            Background="#40000000" CornerRadius="4"
            Visibility="{Binding Path=IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Control Style="{StaticResource WaitSpinnerStyle}" Width="35" Foreground="White" />
            <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="LOADING..." FontWeight="Bold" Margin="0,5" Foreground="White" FontSize="12" />
        </StackPanel>
    </Border>
    <ListBox />
</Grid>

Using a Grid means that the Border will appear on top of the ListBox. In this case, the layer will appear greyed out, and will steal any mouse actions, effectively disabling the underlying ListBox.

Note that the binding here to IsLoaded connects to my view model. I set it to false when I start loading, then again to true when the loading completes. Note that I load my items off the dispatcher thread (on a worker thread) so that the UI updates while I'm doing this work.

like image 182
Drew Noakes Avatar answered Dec 10 '22 23:12

Drew Noakes