I am Trying to implement a ProgressBar
in WPF and figured it would be nice to show the percentage as a percentage on the bar itself.
I found out that the ProgressBar
conveniently already does calculations for the display in the background. So let's say I have a max value of 12 and the current value is 6, is shows a progress bar which is already half/50% full.
Unfortunately the value of the ProgressBar
is still 6 (because it is the sixth element of 12)
Is there an easy way to get the percentage value of the progress bar, or do I have to do the calculations on my own and databind a double
variable to the TextBox
and ProgressBar
?
This is the XAML code I have so far:
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel>
<Grid Margin="20">
<ProgressBar Name="ProgrBar" Minimum="0" Maximum="12" Value="6" Height="30"/>
<TextBlock Text="{Binding ElementName=ProgrBar, Path=Value, StringFormat={}{0}% }" HorizontalAlignment="Center"></TextBlock>
</Grid>
</StackPanel>
</Grid>
</Window>
CodePudding user response:
The ProgressBar
does not know how to display percentages. It has a Minimum
and a Maximum
property that only define the bounds within the Value
is expected. All are of type double
, but their interpretation is up to you. It could be percentages, steps in a process, number of items, you name it.
If the percentage is data from a model in your context, you could expose it from the view model, but if it is only for display purposes, you could create a value converter that takes the limits and value and returns a formatted percentage string.
public class PercentageConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 3 ||
!(values[0] is double minimum) ||
!(values[1] is double maximum) ||
!(values[2] is double value) ||
minimum > maximum ||
value < minimum ||
value > maximum)
return Binding.DoNothing;
var percentage = (value - minimum) / (maximum - minimum);
return percentage.ToString("P0");
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The converter is a multi-value converter to be able to bind multiple properties. It checks the bounds before calculation to rule out weird results in case of invalid input. The format specifier is P0
.
Create an instance of the converter in a resource dictionary in scope and use it in a MultiBinding
.
<Grid Margin="20">
<Grid.Resources>
<local:PercentageConverter x:Key="PercentageConverter"/>
</Grid.Resources>
<ProgressBar Name="ProgrBar" Minimum="0" Maximum="12" Value="6" Height="30"/>
<TextBlock HorizontalAlignment="Center">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource PercentageConverter}">
<Binding ElementName="ProgrBar" Path="Minimum"/>
<Binding ElementName="ProgrBar" Path="Maximum"/>
<Binding ElementName="ProgrBar" Path="Value"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
Since this is a type of control that you might want to reuse, you could think about extracting it into a UserControl
or even custom control if you want to enable templating. Then you could make the display of results toggleable with a property, e.g. DisplayPercentage
or allow for custom converters similar to this one. This could prevent a lot of copy-paste and duplicated code in the long run, keep it DRY.
CodePudding user response:
Is there an easy way to get the percentage value of the progress bar
No, because it's not stored somewhere.
or do I have to do the calculations on my own ...
Yes. Bind the TextBlock
to a property that returns the value of the expression ProgrBar.Maximum / ProgrBar.Value
.