テキストが動的に変化するプログレスバーがあります。進行状況がテキストになり次第、テキストの色が更新されるように、その外観を更新したいと思います。このようなもの。
青色の背景の上に表示されるテキスト(黒)のテキストの色が自動的に白に変わる必要があります。ただし、背景が白のテキストは黒のままにします。
ProgressBars
のデフォルトバージョンTemplate
の変更バージョンを使用してこれを行う1つの方法を次に示します。 2つのTextBlocks
が含まれています
TextBlock
は黒いものですTextBlock
は白いものです。このTextBlock
にはフルコントロールの幅があり、Clip
は進行状況パーツの幅に設定されています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();
}
}
これが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();
}
}
}
次のようになります。
これは迅速で汚い解決策ですが、それは出発点だと思います。お役に立てれば。
私は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();
}
}