web-dev-qa-db-ja.com

動的なテキストとテキストの色の更新を備えた進行状況バー

テキストが動的に変化するプログレスバーがあります。進行状況がテキストになり次第、テキストの色が更新されるように、その外観を更新したいと思います。このようなもの。 enter image description here

青色の背景の上に表示されるテキスト(黒)のテキストの色が自動的に白に変わる必要があります。ただし、背景が白のテキストは黒のままにします。

29
Rohit

ProgressBarsのデフォルトバージョンTemplateの変更バージョンを使用してこれを行う1つの方法を次に示します。 2つのTextBlocksが含まれています

  • 最初のTextBlockは黒いものです
  • 2番目のTextBlockは白いものです。このTextBlockにはフルコントロールの幅があり、Clipは進行状況パーツの幅に設定されています

enter image description here

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

これが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

これは迅速で汚い解決策ですが、それは出発点だと思います。お役に立てれば。

7
user623892

私は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();
    }
}
6
Rohit