2011-03-02 65 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