2011-03-02 7 views
27

Tengo una barra de progreso cuyo texto cambia dinámicamente. Quiero actualizar la apariencia de tal manera que tan pronto como el texto aparezca, el color del texto debería actualizarse. Algo como esto. enter image description hereBarra de progreso con texto dinámico y actualización de color de texto

Necesito texto El texto del texto (negro) que aparece sobre el fondo azul debería cambiar automáticamente a blanco. Sin embargo, el texto con fondo blanco debe permanecer negro.

+1

honestamente, yo no encontrar su idea realmente hermosa. pero una idea: escribe el mismo texto en blanco sobre tu barra de progreso y recorta la parte superpuesta ... l. – fixagon

+0

He adjuntado una instantánea de la única barra de progreso de mi aplicación. No puedo cambiar el comportamiento de la barra de progreso como sugirió ... Cuando vea esta aplicación completa, esto parece funcionar bien, aparte del color del texto no modificable ... – Rohit

+0

Aunque agregué el efecto White Shadow, que parece funcionar bien, pero yo ' Estoy buscando una buena solución. de modo que el color del texto cambie con la barra de progreso completada. – Rohit

Respuesta

44

Aquí hay una manera de hacerlo utilizando una versión modificada del ProgressBars predeterminado ProgressBars predeterminado Template. Contiene dos TextBlocks

  • La primera TextBlock es el negro uno
  • El segundo TextBlock es el blanco. Este TextBlock tiene el ancho del control total y Clip conjunto a la anchura de la parte progreso

enter image description here

El texto de la ProgressBar es vinculante a la propiedad Tag. Utilizable de esta manera.

<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(); 
    } 
} 
+4

+1 ¡Buena solución exclusiva de XAML (a pesar del convertidor de valor)! :) –

+0

Eh, tiendo a considerar los convertidores de valor el costo de hacer negocios de WPF. Al menos encapsulan lindamente bits específicos de lógica, si los escribe/organiza bien ... –

+0

@djacobson: Por supuesto, los convertidores de valor son una gran parte de WPF. Pero me gustan más cuando en realidad están convirtiendo algo y no solo duplican el doble. Pero como las clases de Geometry están usando estructuras por todos lados, se requiere el convertidor Rect –

7

Aquí hay una solución en Silverlight pero debería ser fácil convertirla a WPF.

estoy usando cepillo gradiente lineal de cambiar el color del texto en el bloque de texto, he creado un control de usuario con una barra de progreso y un bloque de texto, vamos a llamarlo "SpecialProgressBar"

Aquí está el 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> 

Y aquí está el código:

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); 
       } 
      } 
     } 
    } 
} 

el último paso es añadir el control de usuario a una página (o en otro control de usuario)

<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> 

Y para probarlo he añadido un temporizador para cambiar el valor de la barra de progreso:

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(); 
     } 
    } 
} 

Se ve así:

enter image description here

Ésta es una solución rápida y sucia pero es un comienzo, creo. Espero que esto ayude.

5

A pesar de que a fuerza utiliza toda la solución proporcionada por Meleak, pero aquí es cómo lo hice.

<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> 

y aquí está convertidor

/// <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(); 
    } 
} 
+0

Aquí el concepto completo es que estoy usando 2 cuadros de texto para mostrar el mismo texto: uno con primer plano en blanco y otro con primer plano en negro. Tanto los cuadros de texto como la barra de progreso tienen el mismo ancho. Ahora corto el cuadro de texto con primer plano blanco a un ancho igual al ancho de relleno de la barra de progreso para que el bloque de texto blanco sea visible solo en la parte donde aparece el relleno de la barra de progreso y el resto del texto es de color negro. – Rohit

+0

Encontré esta pregunta e hice esta respuesta como la que yo necesitaba. No necesité reconstruir la barra de progreso a través de una plantilla de control porque, en este momento, solo hay lugar en la aplicación para ello. Quería algo simple y conciso y esto fue todo para mí. Si necesitamos usarlo en más de un lugar, lo más probable es que lo arroje a un DataTemplate. – blandau

Cuestiones relacionadas