0

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.stack.imgur.com/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>
Coder
  • 121
  • 9
  • Please clarify which value is to be used for what. A progressbar inherits from rangebase https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.primitives.rangebase?view=netframework-4.8 so it has Minimum, Maximum and Value properties. The amount "left" is Maximum - Value. If that's what you need then you'd want a multi converter and multi binding or ViewModel to do the calculation. – Andy Jul 02 '19 at 09:42
  • Yes ,I want to bind the amount left (maximum-value) to "left" textblock . – Coder Jul 02 '19 at 09:44
  • 1
    Not what you are asking, but the way you're doing layout is a bad idea. Don't drag and drop controls around the designer. Edit values in code and use stackpanel, row and column to arrange controls. Never use fixed large margins because they can't flow and your layout will only work on a monitor with the same settings as your own. Which is a bit of a problem when you come to distribute a product. – Andy Jul 02 '19 at 09:46
  • this is not layout just a screenshot to illustrate my question . – Coder Jul 02 '19 at 09:48
  • Wouldn't it be better to have properties in your viewmodel for both the progressbar AND the textblock to bind against, rather than having the controls try to bind against each other? – Kevin Cook Jul 02 '19 at 12:01

1 Answers1

1

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.

Andy
  • 11,864
  • 2
  • 17
  • 20