#1,206 – Changing Color of Custom Circular Progress

A couple of earlier posts (#1,155 and #1,156) demonstrated how to create a simple custom control that displays progress in a circular fashion. A reader of the blog asked how you would change the color of the progress indicator when it got to a certain value. The example below shows how to do that.

Below is the code for the indicator itself. This matches post #1,156 except that we’ve removed the static constructor that overrides the stroke and fill color of the indicator.

    public class CircularProgress : Shape
        // Value (0-100)
        public double Value
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }

        // DependencyProperty - Value (0 - 100)
        private static FrameworkPropertyMetadata valueMetadata =
                new FrameworkPropertyMetadata(
                    0.0,     // Default value
                    null,    // Property changed callback
                    new CoerceValueCallback(CoerceValue));   // Coerce value callback

        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(CircularProgress), valueMetadata);

        private static object CoerceValue(DependencyObject depObj, object baseVal)
            double val = (double)baseVal;
            val = Math.Min(val, 99.999);
            val = Math.Max(val, 0.0);
            return val;

        protected override Geometry DefiningGeometry
                double startAngle = 90.0;
                double endAngle = 90.0 - ((Value / 100.0) * 360.0);

                double maxWidth = Math.Max(0.0, RenderSize.Width - StrokeThickness);
                double maxHeight = Math.Max(0.0, RenderSize.Height - StrokeThickness);

                double xStart = maxWidth / 2.0 * Math.Cos(startAngle * Math.PI / 180.0);
                double yStart = maxHeight / 2.0 * Math.Sin(startAngle * Math.PI / 180.0);

                double xEnd = maxWidth / 2.0 * Math.Cos(endAngle * Math.PI / 180.0);
                double yEnd = maxHeight / 2.0 * Math.Sin(endAngle * Math.PI / 180.0);

                StreamGeometry geom = new StreamGeometry();
                using (StreamGeometryContext ctx = geom.Open())
                        new Point((RenderSize.Width / 2.0) + xStart,
                                  (RenderSize.Height / 2.0) - yStart),
                        true,   // Filled
                        true);  // Closed
                        new Point((RenderSize.Width / 2.0) + xEnd,
                                  (RenderSize.Height / 2.0) - yEnd),
                        new Size(maxWidth / 2.0, maxHeight / 2),
                        0.0,     // rotationAngle
                        (startAngle - endAngle) > 180,   // greater than 180 deg?
                        true,    // isStroked
                    ctx.LineTo(new Point((RenderSize.Width / 2.0), (RenderSize.Height / 2.0)), true, false);

                return geom;

Next is a snippet from the code-behind for a main window. This is just a click handler for a button that updates a property. (Note the use of SetProp method from post #1,205)

        private double _pctComplete = 0.0;
        public double PctComplete
            get { return _pctComplete; }
            set { SetProp(ref _pctComplete, value); }

        private void Button_Click(object sender, RoutedEventArgs e)
            PctComplete = 0.0;

            DispatcherTimer timer = new DispatcherTimer();
            timer.Tick += (s, ea) =>
                PctComplete += 1.0;
                if (PctComplete >= 100.0)
            timer.Interval = new TimeSpan(0, 0, 0, 0, 150);  

We then have a XAML fragment, where we define an instance of the progress indicator. We also set up a data trigger to set the Stroke and Fill properties to change color when they reach a certain level.

        <loc:GreaterThanConverter x:Key="greaterThanConverter"/>
        <Style x:Key="progChangeColor" TargetType="loc:CircularProgress">
            <Setter Property="Stroke" Value="Red"/>
            <Setter Property="Fill" Value="Red"/>
                <DataTrigger Binding="{Binding PctComplete, Converter={StaticResource greaterThanConverter}, ConverterParameter=75}" Value="True">
                    <Setter Property="Stroke" Value="Green"/>
                    <Setter Property="Fill" Value="Green"/>
            Style="{StaticResource progChangeColor}"
             Height="100" Width="100" Margin="5"
             Value="{Binding PctComplete}"
        <ProgressBar Maximum="100"
                 Value="{Binding PctComplete}"
                 Height="25" Margin="10"/>
        <Button Content="Start Timer" Click="Button_Click"

Note that we’re making use of a value converter that takes a value in (e.g. PctComplete) and outputs True when that value passes a certain point (e.g. 75). Here’s the code for that converter:

    public class GreaterThanConverter : IValueConverter
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            return ((double)value) > double.Parse(parameter as string);

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            throw new NotImplementedException();

When everything is wired up and we run this, we see that the indicator starts out red.

And it then turns green when progress gets past 75%.


#1,204 – Using a DataTrigger to Change Content in a ContentPresenter

You can set the ContentTemplate property of a ContentPresenter to a panel containing some content, using a DataTemplate.

This technique becomes even more useful when you have different types of content to display, depending on a particular state in your application. In the example below, we set up a Style for a ContentPresenter that sets default content for the ContentPresenter and then swaps in entirely different content when the JobDone property in the data context becomes true.

        <DataTemplate x:Key="DefaultContent">
                <TextBlock Margin="10" Text="Some default content here.."/>
                <TextBlock Margin="10" Text="Maybe show progress for operation"/>

        <DataTemplate x:Key="AllDoneContent">
                <TextBlock Margin="10" Text="** This is the ALL DONE content..."
                <TextBlock Margin="10" Text="Put anything you like here"/>
                <Button Margin="10" Content="Click Me" HorizontalAlignment="Left"/>

        <Style x:Key="MyContentStyle" TargetType="ContentPresenter">
            <Setter Property="ContentTemplate" Value="{StaticResource DefaultContent}"/>
                <DataTrigger Binding="{Binding JobDone}" Value="True">
                    <Setter Property="ContentTemplate" Value="{StaticResource AllDoneContent}"/>
    <Grid Margin="10">
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>

        <ContentPresenter Grid.Row="0" Style="{StaticResource MyContentStyle}" Content="{Binding}"/>
        <Separator Grid.Row="1"/>

        <CheckBox Grid.Row="2" Margin="10" Content="Mark job done" IsChecked="{Binding JobDone}"/>

Here’s how the application looks in the two states. Note that we can toggle between the two states using the CheckBox.