2011-03-02 58 views
27

我有一个文字动态变化的进度条。我想更新它的外观,以便一旦进展结束文本,文本颜色应该更新。像这样的东西。​​动态文字和文字颜色更新的进度条

我需要出现在蓝色背景上方的文字颜色(黑色)应该自动变为白色。但是具有白色背景的文字应该保持黑色。

+1

说实话,我不找你的想法真的很漂亮。但有一个想法:在你的进度条上用白色书写相同的文本并裁剪覆盖的部分..l。 – fixagon 2011-03-02 13:12:10

+0

我已经附加了从我的应用程序进度条的快照。我不能改变进度条的行为,因为你建议..当你看到这个完整的应用程序,然后这似乎很好,除了文字颜色不可更改.. – Rohit 2011-03-02 13:24:09

+0

虽然我已经添加了白色阴影效果似乎工作良好,但我'米寻找一个不错的解决方案。使文本颜色随着进度条完成而改变。 – Rohit 2011-03-02 13:25:27

回答

44

以下是使用ProgressBars的默认Template的修改版本进行此操作的一种方法。它包含两个TextBlocks

  • 第一TextBlock是黑色的
  • 第二TextBlock是白茫茫的一片。这TextBlock拥有完全控制的进步的一部分

enter image description here

ProgressBar的文本被绑定到Tag财产的宽度和Clip组的宽度。像这样可用。

<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 Nice XAML-only解决方案(显然,值转换器)! :) – 2011-03-03 00:00:45

+0

呃,我只是倾向于考虑价值转换器做WPF业务的成本。至少他们非常巧妙地封装了特定的逻辑位,如果你编写/组织它们... – 2011-03-03 01:05:33

+0

@djacobson:当然,值转换器是WPF的重要组成部分。但是,当他们真的在转换某些东西时,我更喜欢它们,而不是双倍加倍。但由于几何类正在使用遍及整个地方的结构Rect转换器是必需的 – 2011-03-03 08:26:45

7

以下是Silverlight中的解决方案,但应该很容易将其转换为WPF。

我使用线性渐变画笔来更改文本块的颜色,我创建了一个进度条和一个文本框的用户控件,让我们把它称为“SpecialProgressBar”

这里的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> 

而这里的代码:

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

的最后一步是将用户控件添加到页面(或其他用户控制)

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

并对其进行测试我添加了一个计时器来改变进度条值:

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

它看起来像这样:

enter image description here

这是一个快速和肮脏的解决方案但我认为这是一个开始。希望这可以帮助。

5

虽然我力所用Meleak提供的整体解决方案,但这里是我做到了。

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

,这里是转换器

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

这里整个概念是我使用2个文本框显示相同的文本:一个白色的前景和另一个黑色的前景。文本框和进度栏的宽度相同。 现在,我将带有白色前景的文本框剪裁为与进度栏填充宽度相等的宽度,以便仅在显示进度栏填充的部分和文本的其余部分为黑色的部分显示白色文本块。 – Rohit 2011-03-03 18:54:39

+0

我发现这个问题,并作为我的一个答案这个答案。我不需要通过控件模板重建进度条,因为在这个时候,应用程序中只有它的地方。我想要一些简单而简洁的东西,这对我来说很重要。如果我们需要在多个地方使用它,那么我很可能会将它放在DataTemplate中。 – blandau 2013-11-09 04:41:59