wpfdata-bindingexpression-blend-4

How to bind ProgressBar Maximum minus Value to textblock in WPF


In Expression blend, I have one progressBar and two textblocks. I bound the first textblock to the Value property . I need to bind the second textblock to Maximum-Value properties to read the amount left to fill the progressBar. How can I accomplish that in XAML ?

Because I want to see the changes in designer view while I modify progressBar value

Here is the view : https://i.sstatic.net/MqBaq.png

Code:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="mobSizing.MainWindow"
    x:Name="Window"
    Title="MainWindow"
    Width="640" Height="480">

    <Grid x:Name="LayoutRoot">
        <ProgressBar x:Name="progressBar" Height="41" Margin="84,77,125,0" VerticalAlignment="Top" Value="66"/>
        <TextBlock x:Name="Value" HorizontalAlignment="Left" Height="40" Margin="106,142,0,0" TextWrapping="Wrap" Text="{Binding Value, ElementName=progressBar}" VerticalAlignment="Top" Width="182" Foreground="#FF40B617"/>
        <TextBlock x:Name="Left" HorizontalAlignment="Right" Height="40" Margin="0,145,53,0" TextWrapping="Wrap" Text="{Binding Value, ElementName=progressBar}" VerticalAlignment="Top" Width="182" Foreground="#FF8F8F8F"/>
    </Grid>
</Window>

Solution

  • Here's a multiconverter based approach.

    Markup:

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="60"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <ProgressBar x:Name="progressBar" Height="41"  
                     Grid.ColumnSpan="3"
                     Value="66"/>
        <TextBlock x:Name="Value"
                   Grid.Column="0" 
                   Grid.Row="1"
                   Text="{Binding Value, ElementName=progressBar}" VerticalAlignment="Top"  
                   TextAlignment="Center"
                   Foreground="#FF40B617"/>
        <TextBlock x:Name="Left" 
                   Grid.Column="2" 
                   Grid.Row="1"
                   TextAlignment="Center"
                   VerticalAlignment="Top" 
                   Foreground="#FF8F8F8F">
            <TextBlock.Text>
                <MultiBinding Converter="{local:AmountLeftMultiConverter}" StringFormat="{}{0:n0}">
                    <Binding Path="Maximum" ElementName="progressBar"/>
                    <Binding Path="Value"  ElementName="progressBar"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
    </Grid>
    

    The converter will subtract whatever the second binding gives it from the first. These values are passed in an array to a multiconverter.

    The converter. Mine is in a scratch app, so your namespace will be different:

        using System;
        using System.Globalization;
        using System.Windows.Data;
        using System.Windows.Markup;
    
        namespace wpf_99
        {
            public class AmountLeftMultiConverter : MarkupExtension, IMultiValueConverter
            {
                public double Multiplier { get; set; }
    
                public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
                {
                    double toAdd = System.Convert.ToDouble(values[0]);
                    double toSubtract = System.Convert.ToDouble(values[1]);
    
                    return toAdd - toSubtract;
                }
    
                public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
                {
                    throw new NotImplementedException();
                }
    
                public override object ProvideValue(IServiceProvider serviceProvider)
                {
                    return this;
                }
            }
        }
    

    Because this is also a markupextension, you don't need to declare it in any resources before using.