File: parent\Shared\Microsoft\Windows\Themes\ProgressBarBrushConverter.cs
Project: wpf\src\Themes\Classic\PresentationFramework.Classic.csproj (PresentationFramework.Classic)
//---------------------------------------------------------------------------
// File: ProgressBarBrushConverter.cs
//
// Description:
// Converts a brush into a DrawingBrush used to display the "block" style
// progress bar
//
// History:
//  06/28/2004 - t-sergin - Created
//
// Copyright (C) 2004,2005 by Microsoft Corporation.  All rights reserved.
// 
//---------------------------------------------------------------------------
using System;
using System.Globalization;
using System.Threading;
 
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
 
namespace Microsoft.Windows.Themes
{
    /// <summary>
    /// The ProgressBarBrushConverter class
    /// </summary>
    public class ProgressBarBrushConverter : IMultiValueConverter
    {
        /// <summary>
        ///      Creates the brush for the ProgressBar
        /// </summary>
        /// <param name="values">ForegroundBrush, IsIndeterminate, Indicator Width, Indicator Height, Track Width</param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns>Brush for the ProgressBar</returns>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            //
            // Parameter Validation
            //
            Type doubleType = typeof(double);
            if (values == null ||
                (values.Length != 5) ||
                (values[0] == null)  ||
                (values[1] == null)  ||
                (values[2] == null)  ||
                (values[3] == null) ||
                (values[4] == null) ||
                !typeof(Brush).IsAssignableFrom(values[0].GetType()) || 
                !typeof(bool).IsAssignableFrom(values[1].GetType()) ||
                !doubleType.IsAssignableFrom(values[2].GetType()) ||
                !doubleType.IsAssignableFrom(values[3].GetType()) ||
                !doubleType.IsAssignableFrom(values[4].GetType()))
            {
                return null;
            }
         
            //
            // Conversion
            //
 
            Brush brush = (Brush)values[0];
            bool isIndeterminate = (bool)values[1];
            double width = (double)values[2];
            double height = (double)values[3];
            double trackWidth = (double)values[4];
 
            // if an invalid height, return a null brush
            if (width <= 0.0 || Double.IsInfinity(width) || Double.IsNaN(width) ||
                height <= 0.0 || Double.IsInfinity(height) || Double.IsNaN(height) )
            {
                return null;
            }
 
 
            DrawingBrush newBrush = new DrawingBrush();
 
            // Set the viewport and viewbox to the size of the progress region
            newBrush.Viewport = newBrush.Viewbox = new Rect(0, 0, width, height);
            newBrush.ViewportUnits = newBrush.ViewboxUnits = BrushMappingMode.Absolute;
                        
            newBrush.TileMode = TileMode.None;
            newBrush.Stretch = Stretch.None;
 
            DrawingGroup myDrawing = new DrawingGroup();
            DrawingContext myDrawingContext = myDrawing.Open();
 
            double drawnWidth = 0.0; // The total width drawn to the brush so far
 
            double blockWidth = 6.0;
            double blockGap = 2.0;
            double blockTotal = blockWidth + blockGap;
 
            // For the indeterminate case, just draw a portion of the width
            // And animate the brush
            if (isIndeterminate)
            {
                int blocks = (int)Math.Ceiling(width / blockTotal);
 
                // The left (X) starting point of the brush
                double left = -blocks * blockTotal;
 
                // Only draw 30% of the blocks
                double indeterminateWidth = width * .3;
 
                // Generate the brush so it wraps correctly
                // The brush is larger than the rectangle to fill like so:
                //                +-------------+
                // [] [] [] __ __ |[] [] [] __ _|      
                //                +-------------+
                // Translate Brush =>>
                // To have the marquee line up on the left as the blocks are scrolled off to the right
                // we need to have the second set of blocks offset from the first by the width of the rect
                newBrush.Viewport = newBrush.Viewbox = new Rect(left, 0, indeterminateWidth - left, height);
 
                // Add an animated translate transfrom
                TranslateTransform translation = new TranslateTransform();
 
                double milliseconds = blocks * 100; // 100 milliseconds on each position
 
                DoubleAnimationUsingKeyFrames animation = new DoubleAnimationUsingKeyFrames();
                animation.Duration = new Duration(TimeSpan.FromMilliseconds(milliseconds));  // Repeat every 3 seconds
                animation.RepeatBehavior = RepeatBehavior.Forever;
 
                // Add a keyframe to translate by each block
                for (int i = 1; i <= blocks; i++)
                {
                    double x = i * blockTotal;
 
                    animation.KeyFrames.Add(new DiscreteDoubleKeyFrame(x, KeyTime.Uniform));   
                }
 
                // Set the animation to the XProperty
                translation.BeginAnimation(TranslateTransform.XProperty, animation);
 
                // Set the animated translation on the brush
                newBrush.Transform = translation;
 
                // Draw the Blocks to the left of the brush that are translated into view 
                // during the animation
                
                // While able to draw complete blocks,
                while ((drawnWidth + blockWidth) < indeterminateWidth)
                {
                    // Draw a block
                    myDrawingContext.DrawRectangle(
                                brush,
                                null,
                                new Rect(left + drawnWidth, 0, blockWidth, height));
 
                    drawnWidth += blockTotal;
                }
 
                width = indeterminateWidth; //only need to draw 30% of the blocks
                drawnWidth = 0.0; //reset drawn width and draw the left blocks
            }
 
            // Draw as many blocks 
            // While able to draw complete blocks,
            while ( (drawnWidth + blockWidth) < width )
            {
                // Draw a block
                myDrawingContext.DrawRectangle(
                            brush,
                            null,
                            new Rect(drawnWidth, 0, blockWidth, height)); 
                
                drawnWidth += blockTotal;
            }
 
            double remainder = width - drawnWidth;
            // Draw portion of last block when ProgressBar is 100% (ie indicatorWidth == trackWidth)
            if (!isIndeterminate && remainder > 0.0 && Math.Abs(width - trackWidth) < 1.0e-5)
            {
                // Draw incomplete block to fill progress indicator area
                myDrawingContext.DrawRectangle(
                            brush,
                            null,
                            new Rect(drawnWidth, 0, remainder, height));
            }
                       
            myDrawingContext.Close();
            newBrush.Drawing = myDrawing;
 
            return newBrush;
        }
 
        /// <summary>
        /// Not Supported
        /// </summary>
        /// <param name="value">value, as produced by target</param>
        /// <param name="targetTypes">target types</param>
        /// <param name="parameter">converter parameter</param>
        /// <param name="culture">culture information</param>
        /// <returns>Nothing</returns>
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}