我有ProgressBar
的自定义样式,我试图在条形图的开头显示一个椭圆,但是当ProgressBar.Value = 0
椭圆没有显示时,只有当ProgressBar.Value > 5
,有没有办法强制PART_Indicator显示椭圆?
这是一个完整且可重现的示例,只需单击开始按钮即可启动计时器:
欢迎任何想法,提前谢谢!
代码隐藏:
namespace ProgressBar
{
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Threading;
public partial class MainWindow : Window, INotifyPropertyChanged
{
private TimeSpan _elapsedTime;
private TimeSpan _estimatedTotalTime;
private bool _isIndeterminate;
private DispatcherTimer _progressBarTimer;
private double _timeProgress;
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
public TimeSpan ElapsedTime
{
get => _elapsedTime;
set
{
_elapsedTime = value;
NotifyPropertyChanged();
}
}
public TimeSpan EstimatedTotalTime
{
get => _estimatedTotalTime;
set
{
_estimatedTotalTime = value;
NotifyPropertyChanged();
}
}
public bool IsIndeterminate
{
get => _isIndeterminate;
set
{
_isIndeterminate = value;
NotifyPropertyChanged();
}
}
public DispatcherTimer ProgressBarTimer
{
get => _progressBarTimer;
set
{
_progressBarTimer = value;
NotifyPropertyChanged();
}
}
public double TimeProgress
{
get => _timeProgress;
set
{
_timeProgress = value;
NotifyPropertyChanged();
}
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private void OnStart(object sender, RoutedEventArgs routedEventArgs)
{
EstimatedTotalTime = new TimeSpan(0, 0, 60);
ElapsedTime = new TimeSpan();
ProgressBarTimer = new DispatcherTimer(
new TimeSpan(0, 0, 1),
DispatcherPriority.Normal,
OnTick,
Dispatcher.CurrentDispatcher) {
IsEnabled = false
};
ProgressBarTimer.Start();
}
private void OnTick(object sender, EventArgs e)
{
ElapsedTime = ElapsedTime.Add(new TimeSpan(0, 0, 1));
if (ElapsedTime.TotalSeconds.Equals(EstimatedTotalTime.TotalSeconds))
{
IsIndeterminate = true;
return;
}
TimeProgress = ElapsedTime.TotalSeconds * 100 / EstimatedTotalTime.TotalSeconds;
}
}
}
XAML:
<Window x:Class="ProgressBar.MainWindow"
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:local="clr-namespace:ProgressBar"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Window.Resources>
<Style x:Key="LevelMeterProgressBarStyle"
TargetType="{x:Type ProgressBar}">
<Setter Property="Background" Value="#FFBDBDBD" />
<Setter Property="Foreground" Value="#FF348781" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid Name="TemplateRoot">
<Rectangle Name="PART_Track"
Height="5.5"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Fill="{TemplateBinding Background}"
SnapsToDevicePixels="True" />
<Decorator x:Name="PART_Indicator"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0"
Height="5.5"
VerticalAlignment="Center"
Fill="{TemplateBinding Foreground}" />
<Ellipse Grid.Column="1"
Width="33"
Height="33"
VerticalAlignment="Center"
Fill="{TemplateBinding Foreground}"
Stretch="Fill" />
</Grid>
</Decorator>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="2" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<Button Margin="20"
HorizontalAlignment="Center"
Click="OnStart"
Content="Start" />
<TextBlock Margin="20">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0:00}:{1:00}:{2:00} / {3:00}:{4:00}:{5:00}">
<Binding Path="EstimatedTotalTime.Hours" />
<Binding Path="EstimatedTotalTime.Minutes" />
<Binding Path="EstimatedTotalTime.Seconds" />
<Binding Path="ElapsedTime.Hours" />
<Binding Path="ElapsedTime.Minutes" />
<Binding Path="ElapsedTime.Seconds" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<ProgressBar Margin="20"
IsIndeterminate="{Binding IsIndeterminate}"
Maximum="100"
Minimum="0"
Style="{DynamicResource LevelMeterProgressBarStyle}"
Value="{Binding TimeProgress}" />
</StackPanel>
</Window>
答案 0 :(得分:2)
我觉得这并不容易。进度条指示符的宽度是根据此公式以私有方法设置的:
this._indicator.Width = (this.IsIndeterminate || maximum <= minimum ? 1.0 : (num - minimum) / (maximum - minimum)) * this._track.ActualWidth;
您必须根据RangeBase
编写自己的自定义进度条答案 1 :(得分:1)
最快的解决方案是在MinWidth="33"
上设置"PART_Indicator"
- MinWidth
优先于Width
,因此椭圆将始终完全可见。这种方法的缺点是椭圆将保持“静止”,直到值足够高,以使指示器的总宽度高于椭圆的宽度(我认为在你的情况下它是5),我认为这是不合需要的
解决方案的关键在于将椭圆移动到"PART_Indicator"
之外,如@GenericTeaCup所述。这个想法是设置轨道条的水平边距和指标等于椭圆宽度的一半,这样当值最小和最大时,它的左右两侧正好在椭圆的中心点之下。这样我们就可以轻松地将椭圆移动到指示器的实际宽度,并使其始终位于我们想要的确切位置(直接位于指示器右侧的中心)。现在我不确定你对赛道栏的开始和结束有什么期望,所以我会给你两个我认为最常见的选项。
第一个看起来像这样(我为椭圆增加了一些透明度,以便明显区别):
此模板应产生描述结果:
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid x:Name="TemplateRoot">
<Rectangle x:Name="LeftFill" Height="5.5" Width="17" HorizontalAlignment="Left" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" SnapsToDevicePixels="True" />
<Rectangle x:Name="RightFill" Height="5.5" Width="17" HorizontalAlignment="Right" VerticalAlignment="Center" Fill="{TemplateBinding Background}" SnapsToDevicePixels="True" />
<Rectangle x:Name="PART_Track" Height="5.5" Margin="16.5,0" HorizontalAlignment="Stretch" VerticalAlignment="Center" Fill="{TemplateBinding Background}" SnapsToDevicePixels="True" />
<Rectangle x:Name="PART_Indicator" Height="5.5" Margin="16.5,0" HorizontalAlignment="Left" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" SnapsToDevicePixels="True" />
<Ellipse Width="33" Height="33" HorizontalAlignment="Left" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" Stretch="Fill" SnapsToDevicePixels="True">
<Ellipse.RenderTransform>
<TranslateTransform X="{Binding Source={x:Reference PART_Indicator}, Path=ActualWidth}" />
</Ellipse.RenderTransform>
</Ellipse>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2" SnapsToDevicePixels="True" />
</Grid>
</ControlTemplate>
第二个选项是:
为了获得它,您只需从模板中删除"LeftFill"
和"RightFill"
即可。请注意,跟踪栏似乎相对于ProgressBar
本身缩进。
如果您对在椭圆上使用RenderTransform
感到不舒服,可以将其放在Canvas
并绑定Canvas.Left
属性中。