wpf 带有动态文本和文本颜色更新的进度条

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5167867/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:37:59  来源:igfitidea点击:

Progress bar with dynamic text & text color update

wpfprogress-bar

提问by Rohit

I've a progressbar whose text changes dynamically. I want to update the appearance of it such that as soon as progress comes over text then text color should update. Something like this. enter image description here

我有一个进度条,它的文本会动态变化。我想更新它的外观,这样一旦进度超过文本,文本颜色就会更新。像这样的东西。在此处输入图片说明

I need text color of text (black) appearing above blue background should automatically change to white. However text having white background should remain black.

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

回答by Fredrik Hedblad

Here is one way to do it using a modified version of the ProgressBarsdefault Template. It contains two TextBlocks

这是使用ProgressBars默认Template. 它包含两个TextBlocks

  • The first TextBlockis the black one
  • The second TextBlockis the white one. This TextBlockhas the Width of the full control and Clipset to the Width of the progress part
  • 第一个TextBlock是黑色的
  • 第二个TextBlock是白色的。这TextBlock具有完全控制Clip的宽度并设置为进度部分的宽度

enter image description here

在此处输入图片说明

The text of the ProgressBaris binding to the Tagproperty. Usable like this.

的文本ProgressBar绑定到Tag属性。可以这样用。

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

回答by Rohit

Here's a solution in Silverlight but it should be easy to convert it to WPF.

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

I'm using linear gradient brush to change the text color in the text block, I created a user control with a progress bar and a text block, let's call it "SpecialProgressBar"

我正在使用线性渐变画笔来更改文本块中的文本颜色,我创建了一个带有进度条和文本块的用户控件,我们称之为“SpecialProgressBar”

Here's the XAML:

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

And here's the 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);
                }
            }
        }
    }
}

The last step is to add the user control to a page (or another user control)

最后一步是将用户控件添加到页面(或另一个用户控件)

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

And to test it out I added a timer to change the progress bar value:

为了测试它,我添加了一个计时器来更改进度条值:

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

It looks like this:

它看起来像这样:

enter image description here

在此处输入图片说明

This is a quick and dirty solution but it's a start, I think. Hope this helps.

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

回答by Rohit

Though I dint used the whole solution provided by Meleak, but here is how i did it.

虽然我使用了 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>

and here is converter

这是转换器

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