web-dev-qa-db-fra.com

Barre de progression avec mise à jour dynamique du texte et des couleurs du texte

J'ai une barre de progression dont le texte change dynamiquement. Je souhaite mettre à jour l'apparence de celui-ci de sorte que dès que le texte progresse, la couleur du texte soit mise à jour. Quelque chose comme ça. enter image description here

J'ai besoin que la couleur du texte (noir) apparaissant au-dessus du fond bleu devienne automatiquement blanc. Cependant, le texte sur fond blanc doit rester noir.

29
Rohit

Voici une façon de le faire en utilisant une version modifiée de ProgressBars par défaut Template. Il contient deux TextBlocks

  • Le premier TextBlock est le noir
  • Le second TextBlock est le blanc. Ce TextBlock a la largeur du contrôle total et Clip défini sur la largeur de la partie de progression

enter image description here

Le texte de ProgressBar se lie à la propriété Tag. Utilisable comme ça.

<ProgressBar TextBlock.FontWeight="Bold"
             Tag="ProgressBar Text"
             Foreground="Blue"
             Style="{DynamicResource MyProgressBarStyle}"/>

MyProgressBarStyle

<LinearGradientBrush x:Key="ProgressBarBackground" EndPoint="1,0" StartPoint="0,0">
    <GradientStop Color="#BABABA" Offset="0"/>
    <GradientStop Color="#C7C7C7" Offset="0.5"/>
    <GradientStop Color="#BABABA" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarBorderBrush" EndPoint="0,1" StartPoint="0,0">
    <GradientStop Color="#B2B2B2" Offset="0"/>
    <GradientStop Color="#8C8C8C" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarGlassyHighlight" EndPoint="0,1" StartPoint="0,0">
    <GradientStop Color="#50FFFFFF" Offset="0.5385"/>
    <GradientStop Color="#00FFFFFF" Offset="0.5385"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarTopHighlight" EndPoint="0,1" StartPoint="0,0">
    <GradientStop Color="#80FFFFFF" Offset="0.05"/>
    <GradientStop Color="#00FFFFFF" Offset="0.25"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorAnimatedFill" EndPoint="1,0" StartPoint="0,0">
    <GradientStop Color="#00FFFFFF" Offset="0"/>
    <GradientStop Color="#60FFFFFF" Offset="0.4"/>
    <GradientStop Color="#60FFFFFF" Offset="0.6"/>
    <GradientStop Color="#00FFFFFF" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorDarkEdgeLeft" EndPoint="1,0" StartPoint="0,0">
    <GradientStop Color="#0C000000" Offset="0"/>
    <GradientStop Color="#20000000" Offset="0.3"/>
    <GradientStop Color="#00000000" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorDarkEdgeRight" EndPoint="1,0" StartPoint="0,0">
    <GradientStop Color="#00000000" Offset="0"/>
    <GradientStop Color="#20000000" Offset="0.7"/>
    <GradientStop Color="#0C000000" Offset="1"/>
</LinearGradientBrush>
<RadialGradientBrush x:Key="ProgressBarIndicatorLightingEffectLeft" RadiusY="1" RadiusX="1" RelativeTransform="1,0,0,1,0.5,0.5">
    <GradientStop Color="#60FFFFC4" Offset="0"/>
    <GradientStop Color="#00FFFFC4" Offset="1"/>
</RadialGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorLightingEffect" EndPoint="0,0" StartPoint="0,1">
    <GradientStop Color="#60FFFFC4" Offset="0"/>
    <GradientStop Color="#00FFFFC4" Offset="1"/>
</LinearGradientBrush>
<RadialGradientBrush x:Key="ProgressBarIndicatorLightingEffectRight" RadiusY="1" RadiusX="1" RelativeTransform="1,0,0,1,-0.5,0.5">
    <GradientStop Color="#60FFFFC4" Offset="0"/>
    <GradientStop Color="#00FFFFC4" Offset="1"/>
</RadialGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorGlassyHighlight" EndPoint="0,1" StartPoint="0,0">
    <GradientStop Color="#90FFFFFF" Offset="0.5385"/>
    <GradientStop Color="#00FFFFFF" Offset="0.5385"/>
</LinearGradientBrush>
<Style x:Key="MyProgressBarStyle" TargetType="{x:Type ProgressBar}">
    <Setter Property="Foreground" Value="#01D328"/>
    <Setter Property="Background" Value="{StaticResource ProgressBarBackground}"/>
    <Setter Property="BorderBrush" Value="{StaticResource ProgressBarBorderBrush}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ProgressBar}">
                <Grid x:Name="TemplateRoot" SnapsToDevicePixels="true">
                    <TextBlock Text="{TemplateBinding Tag}" Grid.ZIndex="2" Foreground="Black"
                                        HorizontalAlignment="Center"
                                        VerticalAlignment="Center"/>
                    <TextBlock Text="{TemplateBinding Tag}"
                                Grid.ZIndex="3" Foreground="White"
                                Width="{Binding ElementName=rectangle, Path=ActualWidth}"
                                TextAlignment="Center"
                                HorizontalAlignment="Stretch"
                                VerticalAlignment="Center">
                        <TextBlock.Clip>
                            <RectangleGeometry>
                                <RectangleGeometry.Rect>
                                    <MultiBinding Converter="{StaticResource RectConverter}">
                                        <Binding ElementName="Indicator" Path="ActualWidth"/>
                                        <Binding ElementName="Indicator" Path="ActualHeight"/>
                                    </MultiBinding>
                                </RectangleGeometry.Rect>
                            </RectangleGeometry>
                        </TextBlock.Clip>
                    </TextBlock>
                    <Rectangle x:Name="rectangle" Fill="{TemplateBinding Background}" RadiusY="2" RadiusX="2"/>
                    <Border Background="{StaticResource ProgressBarGlassyHighlight}" CornerRadius="2" Margin="1"/>
                    <Border BorderBrush="#80FFFFFF" BorderThickness="1,0,1,1" Background="{StaticResource ProgressBarTopHighlight}" Margin="1"/>
                    <Rectangle x:Name="PART_Track" Margin="1"/>
                    <Decorator x:Name="PART_Indicator" HorizontalAlignment="Left" Margin="1">
                        <Grid x:Name="Foreground">
                            <Rectangle x:Name="Indicator" Fill="{TemplateBinding Foreground}"/>
                            <Grid x:Name="Animation" ClipToBounds="true">
                                <Rectangle x:Name="PART_GlowRect" Fill="{StaticResource ProgressBarIndicatorAnimatedFill}" HorizontalAlignment="Left" Margin="-100,0,0,0" Width="100"/>
                            </Grid>
                            <Grid x:Name="Overlay">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition MaxWidth="15"/>
                                    <ColumnDefinition Width="0.1*"/>
                                    <ColumnDefinition MaxWidth="15"/>
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <Rectangle x:Name="LeftDark" Fill="{StaticResource ProgressBarIndicatorDarkEdgeLeft}" Margin="1,1,0,1" RadiusY="1" RadiusX="1" Grid.RowSpan="2"/>
                                <Rectangle x:Name="RightDark" Grid.Column="2" Fill="{StaticResource ProgressBarIndicatorDarkEdgeRight}" Margin="0,1,1,1" RadiusY="1" RadiusX="1" Grid.RowSpan="2"/>
                                <Rectangle x:Name="LeftLight" Grid.Column="0" Fill="{StaticResource ProgressBarIndicatorLightingEffectLeft}" Grid.Row="2"/>
                                <Rectangle x:Name="CenterLight" Grid.Column="1" Fill="{StaticResource ProgressBarIndicatorLightingEffect}" Grid.Row="2"/>
                                <Rectangle x:Name="RightLight" Grid.Column="2" Fill="{StaticResource ProgressBarIndicatorLightingEffectRight}" Grid.Row="2"/>
                                <Border x:Name="Highlight1" Background="{StaticResource ProgressBarIndicatorGlassyHighlight}" Grid.ColumnSpan="3" Grid.RowSpan="2"/>
                                <Border x:Name="Highlight2" Background="{StaticResource ProgressBarTopHighlight}" Grid.ColumnSpan="3" Grid.RowSpan="2"/>
                            </Grid>
                        </Grid>
                    </Decorator>
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="Orientation" Value="Vertical">
                        <Setter Property="LayoutTransform" TargetName="TemplateRoot">
                            <Setter.Value>
                                <RotateTransform Angle="-90"/>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                    <Trigger Property="IsIndeterminate" Value="true">
                        <Setter Property="Visibility" TargetName="LeftDark" Value="Collapsed"/>
                        <Setter Property="Visibility" TargetName="RightDark" Value="Collapsed"/>
                        <Setter Property="Visibility" TargetName="LeftLight" Value="Collapsed"/>
                        <Setter Property="Visibility" TargetName="CenterLight" Value="Collapsed"/>
                        <Setter Property="Visibility" TargetName="RightLight" Value="Collapsed"/>
                        <Setter Property="Visibility" TargetName="Indicator" Value="Collapsed"/>
                    </Trigger>
                    <Trigger Property="IsIndeterminate" Value="false">
                        <Setter Property="Background" TargetName="Animation" Value="#80B5FFA9"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

RectConverter

public class RectConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double width = (double)values[0];
        double height = (double)values[1];
        return new Rect(0, 0, width, height);
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
45
Fredrik Hedblad

Voici une solution dans Silverlight mais il devrait être facile de la convertir en WPF.

J'utilise un pinceau de dégradé linéaire pour changer la couleur du texte dans le bloc de texte, j'ai créé un contrôle utilisateur avec une barre de progression et un bloc de texte, appelons-le "SpecialProgressBar"

Voici le XAML:

<UserControl x:Class="TestSilverlightApplication.SpecialProgressBar"
             xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.Microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             d:DesignHeight="300"
             d:DesignWidth="400"
             x:Name="specialProgressBar">

    <Canvas  Width="Auto"
             Height="Auto">

        <ProgressBar Name="progressBar"
                     IsIndeterminate="False"
                     Background="White"
                     Foreground="Blue"
                     Height="{Binding Height, ElementName=specialProgressBar}"
                     Width="{Binding Width, ElementName=specialProgressBar}" />

        <TextBlock x:Name="textBlock"
                   FontWeight="Bold"
                   Text="xxx of yyy" />
    </Canvas>
</UserControl>

Et voici le code:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace TestSilverlightApplication
{
    public partial class SpecialProgressBar : UserControl
    {
        private Point _textBlockPosition;
        private readonly LinearGradientBrush _linearGradientBrush;
        private readonly GradientStop _gradientStop;

        public SpecialProgressBar()
        {
            InitializeComponent();

            // will be changing this gradient stop as the progress bar value changes
            _gradientStop = new GradientStop
            {
                Color = Colors.Black,
                Offset = 0
            };

            // the default brush we want to start with,
            // you might want to play with the start point x value to get the effect you want
            _linearGradientBrush = new LinearGradientBrush
            {
                StartPoint = new Point(-0.2, 0.5),
                EndPoint = new Point(1, 0.5),
                GradientStops = new GradientStopCollection
                {
                    _gradientStop,
                    new GradientStop
                    {
                        Color = Colors.Black,
                        Offset = 1
                    }
                }
            };

            // set the brush to the text block 
            textBlock.Foreground = _linearGradientBrush;

            Loaded += new RoutedEventHandler(SpecialProgressBar_Loaded);
            progressBar.ValueChanged += new RoutedPropertyChangedEventHandler<double>(progressBar_ValueChanged);
        }

        private void SpecialProgressBar_Loaded(object sender, RoutedEventArgs e)
        {
            // center text block on top of the progress bar
            _textBlockPosition = new Point(progressBar.Width / 2 - textBlock.ActualWidth / 2,
                                           progressBar.Height / 2 - textBlock.ActualHeight / 2);

            textBlock.SetValue(Canvas.LeftProperty, _textBlockPosition.X);
            textBlock.SetValue(Canvas.TopProperty, _textBlockPosition.Y);
        }

        private void progressBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            // print out the value in the text block
            textBlock.Text = string.Concat(e.NewValue, " of ", progressBar.Maximum);

            // get the value relative to the size of the progress bar
            var x = e.NewValue / progressBar.Maximum * progressBar.Width;             

            // if the value is equal to or greater than the position of the text block on the canvas (on the progress bar)
            // then we want to change the gradient offset and color.
            if (x >= _textBlockPosition.X)
            {
                _gradientStop.Offset += 0.1 * textBlock.ActualWidth / progressBar.Width;
                _gradientStop.Color = Colors.White;

                // when we pass the end of the text block we don't need the gradient any more,
                // replace it with a solid white color
                if (_gradientStop.Offset >= 1)
                {
                    textBlock.Foreground = new SolidColorBrush(Colors.White);
                }
            }
        }
    }
}

La dernière étape consiste à ajouter le contrôle utilisateur à une page (ou à un autre contrôle utilisateur)

<UserControl x:Class="TestSilverlightApplication.MainPage"
             xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.Microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:TestSilverlightApplication="clr-namespace:TestSilverlightApplication"
             mc:Ignorable="d">

    <Grid>
        <TestSilverlightApplication:SpecialProgressBar x:Name="specialProgressBar"
                                                       Width="200"
                                                       Height="40" />
    </Grid>
</UserControl>

Et pour le tester, j'ai ajouté une minuterie pour changer la valeur de la barre de progression:

using System;
using System.Windows.Controls;
using System.Windows.Threading;

namespace TestSilverlightApplication
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();

            this.Loaded += new System.Windows.RoutedEventHandler(MainPage_Loaded);
        }

        private void MainPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            var timer = new DispatcherTimer();
            timer.Tick += (s, args) => specialProgressBar.progressBar.Value += 1;
            timer.Interval = new TimeSpan(1000000);
            timer.Start();
        }
    }
}

Cela ressemble à ceci:

enter image description here

C'est une solution rapide et sale mais c'est un début, je pense. J'espère que cela t'aides.

7
user623892

Bien que j'aie utilisé la solution complète fournie par Meleak, voici comment je l'ai fait.

<ProgressBar x:Name="SummaryProgressBar"
                         BorderBrush="Black"
                         BorderThickness="1"
                         Background="LightGray"
                         FlowDirection="LeftToRight"
                         Maximum="1"
                         MinWidth="200"
                         Width="Auto">
                <ProgressBar.Value>
                    <MultiBinding Converter="{StaticResource ArithmeticConverter}"
                                  Mode="OneWay">
                        <Binding Path="ABCCollectionView.Count"/>
                        <Binding Source="{StaticResource DivideArithmeticSymbol}" />
                        <Binding Path="XYZCollectionView.Count"/>
                    </MultiBinding>
                </ProgressBar.Value>
            </ProgressBar>
            <!-- Black Progress Bar Text -->
            <TextBlock x:Name="TextBlockBlack"
                       VerticalAlignment="Center"
                       TextAlignment="Center"
                       HorizontalAlignment="Stretch"
                       FontWeight="Bold"
                       Foreground="Black"
                       Text="{Binding SummaryText}"
                       Width="{Binding ElementName=SummaryProgressBar, Path=ActualWidth}"></TextBlock>

            <!-- White Progress Bar Text -->
            <TextBlock x:Name="TextBlockWhite"
                       VerticalAlignment="Center"
                       TextAlignment="Center"
                       HorizontalAlignment="Stretch"
                       FontWeight="Bold"
                       Foreground="White"
                       Text="{Binding SummaryText}"
                       Width="{Binding ElementName=SummaryProgressBar, Path=ActualWidth}">
                <TextBlock.Clip>
                    <RectangleGeometry>
                        <RectangleGeometry.Rect>
                            <MultiBinding Converter="{StaticResource ProgressBarFillToRectConverter}">
                                    <Binding ElementName="SummaryProgressBar" Path="Value"/>
                                    <Binding ElementName="TextBlockWhite" Path="ActualWidth" />
                                    <Binding ElementName="TextBlockWhite" Path="ActualHeight"/>
                                </MultiBinding>
                        </RectangleGeometry.Rect>
                    </RectangleGeometry>
                </TextBlock.Clip>
            </TextBlock>

et voici le convertisseur

 /// <summary>
/// Converts the ProgressBar Fill percentage width to a Rectangle whose width is calculated by multiplying Fill Percentage to Actual Width of control. Height is passed too.
/// Note: This converter is used in showing WHITE & BLACK text on progress bar. Also use White textblock next to Black not reverse in XAML.
/// </summary>
public class ProgressBarFillToRectConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values != null && values[0] != null && values[1] != null && values[2] != null)
        {
            double progressBarFillPercentage = (double)values[0];
            double textBlockActualyWidth = (double)values[1];
            double textBlockHeight = (double)values[2];
            return new Rect(0, 0, progressBarFillPercentage * textBlockActualyWidth, textBlockHeight); // ProgressBarFillWidth is calculated by multiplying Fill 
            // percentage with actual width
        }
        return new Rect(0, 0, 0, 0); // Default Zero size rectangle

    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
6
Rohit