A spinner and progress for xaml, wpf.

I wanted a spinner control, and a nice one. Google searches did not help, although heaps of recommendations and a few free controls could be found, non of them was satisfying enough for what I wanted to do. And of-course I didn’t want some C# code-behind. So I opened XamlPadX and started building one for myself.

Result is something like this and I am happy with it:

It is nothing more than a template for ProgressBar. By the way, setting the IsInditerminate  to True makes the number in the middle to disappear, resulting a generic wait spinner.

Below is the xaml to achieve the spinner progress bar, you can put it some where in your resources and use it:

<Grid.Resources>
 <Style TargetType="ProgressBar" x:Key="SpinnerProgress">
 <Setter Property="Template">
 <Setter.Value>
 <ControlTemplate TargetType="ProgressBar">
 <Grid>
 <ProgressBar x:Name="pgBar" Value="{TemplateBinding Value}" Visibility="Collapsed" IsIndeterminate="{TemplateBinding IsIndeterminate}"/>
 <TextBox Background="Red" Text="{Binding ElementName=pgBar, Path=Value}">
 <TextBox.Style>
 <Style TargetType="{x:Type TextBox}">
 <Setter Property="Template">
 <Setter.Value>
 <ControlTemplate TargetType="TextBox">
 <Grid>
 <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal">
 <TextBlock Text="{TemplateBinding Text}">
 <TextBlock.Style>
 <Style TargetType="TextBlock">
 <Style.Triggers>
 <DataTrigger Binding="{Binding ElementName=pgBar, Path=IsIndeterminate}" Value="True">
 <Setter Property="Visibility" Value="Collapsed"/>
 </DataTrigger>
 </Style.Triggers>
 </Style>
 </TextBlock.Style>
 <TextBlock Text="%"/>
 </TextBlock>
 </StackPanel>
 <Grid Width="50" Height="50" x:Name="mainGrid">
 <Grid.RenderTransform>
 <RotateTransform Angle="0" CenterX="25" CenterY="25" />
 </Grid.RenderTransform>
 <Grid.Triggers>
 <EventTrigger RoutedEvent="Grid.Loaded">
 <BeginStoryboard>
 <Storyboard RepeatBehavior="Forever">
 <DoubleAnimation From="0" To="360" Duration="00:00:00.750" Storyboard.TargetName="mainGrid" Storyboard.TargetProperty="(Grid.RenderTransform).(RotateTransform.Angle)" />
 </Storyboard>
 </BeginStoryboard>
 </EventTrigger>
 </Grid.Triggers>

<Border x:Name="spinnerMask" BorderThickness="6" CornerRadius="25" BorderBrush="White"/>
 <Rectangle>
 <Rectangle.OpacityMask>
 <VisualBrush Visual="{Binding ElementName=spinnerMask}"/>
 </Rectangle.OpacityMask>
 <Rectangle.Fill>
 <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
 <GradientStop Color="#FF068206" Offset="0" />
 <GradientStop Color="#FF72CE72" Offset="0.198" />
 <GradientStop Color="#FFC2FFC2" Offset="0.48" />
 <GradientStop Color="#FFC2FFC2" Offset="0.52" />
 <GradientStop Color="#FF72CE72" Offset="0.891" />
 <GradientStop Color="#FF068206" Offset="1" />
 </LinearGradientBrush>
 </Rectangle.Fill>
 </Rectangle>
 <Rectangle Fill="#FF068206">
 <Rectangle.OpacityMask>
 <VisualBrush Visual="{Binding ElementName=spinnerMask}"/>
 </Rectangle.OpacityMask>
 <Rectangle.Clip>
 <RectangleGeometry Rect="0,0,50,25"/>
 </Rectangle.Clip>
 </Rectangle>
 </Grid>
 </Grid>
 </ControlTemplate>
 </Setter.Value>
 </Setter>
 </Style>
 </TextBox.Style>
 </TextBox>
 </Grid>
 </ControlTemplate>
 </Setter.Value>
 </Setter>
 </Style>
 </Grid.Resources>

And here is how to use it:

<ProgressBar Style="{StaticResource ResourceKey=SpinnerProgress}" IsIndeterminate="False" Value="12"/>
Advertisement

Xaml localization: Using .resx Resources in Xaml without x:static

The Microsoft recommended approach to xaml localization is using the locbaml tool. To be honest, it sucks! It has a lot of manual work and is not compatible with previous models.

Many companies have already lots of infrastructure in place for automatic support of localization. Replacing the whole infrastructure, for just a few recently added Silverlight or WPF projects does not make sense.

People give various suggestions, first thing you hear is to use x:static and .resx files. It is alright, and if it works for you then bingo. But designer does not play well with x:static, also it only works if you use the default Visual Studio resource generator to generate classes. In general I don’t like this approach.

Good news is that Dynamic objects are lovely sometimes. Xaml can use them a lot, so I create a dynamic object called ResourceLoader, and use it to load my resources from the .resx file. Then I can use binding (withe the mode=OneTime for better performance) to load resources for the right local.

<TextBlock x:Name="LoginLable" Text="{Binding Source={StaticResource ResourceKey=Res},Path=LogonText, Mode=OneTime}">
</TextBlock>

The only additional thing is to add the static resource which is the ResourceLoader somewhere in the logical tree.

<local:ResourceLoader x:Key="Res" Assembly="LogonDialog" />

The attribute “Assembly” defines the assembly which resource should be loaded from, otherwise the ResourceLoader assembly would be selected which can be wrong depending on the solution structure and location of resources.

You can download the ResourceLoader.cs from here.

Please note that you should rename the above file ResourceLoader.cs, because wordpress does not allow uploading .cs files.