'Edit progress bar shape wpf

I have a progress bar whose ControlTemplate is as follows and I want to set the value of the progress bar to a number so the progress updates to that value. I refered to the accpeted answer on this post but don't know how to use the converter for my scenario. I also checked this post and editing the Rect works fine. But I can't use the Rectangle Geomerty. Please help. (If I remove the commented Paths in the xaml below, it creates some value that fills the container.) This is the screenshot if I remove the commented xaml.

enter image description here

<Window.Resources>
        <ControlTemplate x:Key="ProgressBarPath" TargetType="ProgressBar">
            <Viewbox xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Stretch="Uniform">
                <Canvas Name="Test" Width="100" Height="100" Canvas.Left="0" Canvas.Top="0">
                    <Canvas.RenderTransform>
                        <TranslateTransform X="0" Y="0"/>
                    </Canvas.RenderTransform>
                    <Canvas.Resources/>
                    <Canvas Name="g40">
                        <!--<Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path28" 
                          Fill="Red">
                            <Path.Data>
                                <PathGeometry Figures="M35.1 57h-.2l-2.1 2.6-10 12.5c-2.1 2.7-5.2 6.2-4.3 9.5 1.7 6.1 7.6 6.1 11.8 6.1H50V57H35.7z" FillRule="NonZero"/>
                            </Path.Data>
                        </Path>-->
                        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path30" Fill="#FF333333">
                            <Path.Data>
                                <PathGeometry Figures="M30.8 87.7c-4.2 0-10.1 0-11.8-6.1-.9-3.3 2.2-6.8 4.3-9.5l10-12.5 2.1-2.6 5-6.3 1.6-2.1 1-1.3V17.5l-2.2-.2c-1.2-.1-2.1-1.1-2.1-2.4 0-1.3 1.1-2.4 2.4-2.4H50v-5h-9.2c-.9 0-1.7.3-2.2.4-2.8 1-4.9 3.8-4.9 7 0 3 1.8 5.6 4.4 6.7v24.2L29.3 57 16.7 72.8c-2.9 3.7-3.5 8.6-1.4 12.8 2 4.2 6.2 6.9 10.9 6.9H50v-4.8H30.8z" FillRule="NonZero"/>
                            </Path.Data>
                        </Path>
                        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path32" Fill="#FF333333">
                            <Path.Data>
                                <PathGeometry Figures="M35.4 57l-2.1 2.6 2.1-2.6z" FillRule="EvenOdd"/>
                            </Path.Data>
                        </Path>
                        <!--<Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path34" 
                              Fill="Red">
                            <Path.Data>
                                <PathGeometry Figures="M64.3 57H50v30.7h19.2c4.2 0 10.7-.7 11.8-6.1.7-3.4-2.2-6.8-4.3-9.5l-10-12.5-2.1-2.6h-.3z" 
                                          FillRule="NonZero"/>
                            </Path.Data>
                        </Path>-->
                        <Path xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="path36" Fill="#FF666666">
                            <Path.Data>
                                <PathGeometry Figures="M84.7 85.6c2-4.2 1.5-9.1-1.4-12.8L70.7 57l-8.8-11.1V21.7c2.6-1.1 4.4-3.8 4.4-6.8 0-4.1-3.3-7.4-7.4-7.4H50v5h8.9c1.3 0 2.4 1.1 2.4 2.5 0 1.2-.9 2.2-2.1 2.3l-2.2.2v29.8l1 1.3 1.6 2.1 7.1 8.9 10 12.5c2.1 2.7 5 6.1 4.3 9.5-1.1 5.3-7.6 6.1-11.8 6.1H50v4.8h23.9c4.6 0 8.8-2.6 10.8-6.9z" FillRule="NonZero"/>
                            </Path.Data>
                        </Path>
                        <!--Unknown tag: metadata-->
                    </Canvas>
                </Canvas>
            </Viewbox>
        </ControlTemplate>
    </Window.Resources>
    <Grid HorizontalAlignment="Center" Margin="0 4 0 0">
        <ProgressBar Template="{StaticResource ProgressBarPath}" Width="480" Height="102" Value="40" />
    </Grid>
</Window>


Solution 1:[1]

If the bar is a rectangular shape, you can utilize the built-in mechanism of ProgressBar to change the shape to show current value. Otherwise, you will need to implement the mechanism on your own.

I think Clemens's answer will give a good hint for this. Let's say, you create a custom ProgressBar named "FlaskProgressBar". In the CustomTemplate for this ProgressBar,

  1. Define a PathGeometry for the Path of outline and set its Stroke two-colored Brush to draw outline.

  2. Add a RectangleGeometry for the Path of vacant area and crop it using the Data of the Path of outline.

  3. Add a RectangleGeometry for the Path of filled area which shows current value and crop it as well. Name the RectangleGeomety "PART_Content" so that it can be found from the code-behind.

<ControlTemplate x:Key="FlaskProgressBarTemplate" TargetType="{x:Type local:FlaskProgressBar}">
    <ControlTemplate.Resources>
        <LinearGradientBrush x:Key="OutlineBrush" StartPoint="0,0" EndPoint="1,0">
            <GradientStop Color="#FF333333" Offset="0.5"/>
            <GradientStop Color="#FF666666" Offset="0.5"/>
            <GradientStop Color="#FF666666" Offset="1"/>
        </LinearGradientBrush>
    </ControlTemplate.Resources>
    <Grid>
        <Grid Margin="6">
            <!-- Draw outline -->
            <Path x:Name="Outline"
                  StrokeThickness="8"
                  Stroke="{StaticResource OutlineBrush}">
                <Path.Data>
                    <PathGeometry Figures="M 20,0 L 20,60 0,100 60,100 40,60 40,0 Z"/>
                </Path.Data>
            </Path>
            <!-- Draw vacent area -->
            <Path Data="{Binding Data, ElementName=Outline}"
                  Fill="{TemplateBinding Background}">
                <Path.Clip>
                    <RectangleGeometry Rect="0,0,60,100"/>
                </Path.Clip>
            </Path>
            <!-- Draw filled area -->
            <Path Data="{Binding Data, ElementName=Outline}"
                  Fill="{TemplateBinding Foreground}">
                <Path.Clip>
                    <RectangleGeometry x:Name="PART_Content" Rect="0,100,60,100"/>
                </Path.Clip>
            </Path>
        </Grid>
    </Grid>
</ControlTemplate>

In the "FlaskProgressBar", find the RectangleGeometry named "PART_Content" and when current value is changed, change Y value of its Rect to move it up or down.

[TemplatePart(Name = "PART_Content", Type = typeof(RectangleGeometry))]
public class FlaskProgressBar : ProgressBar
{
    public FlaskProgressBar() : base()
    {
        ValueProperty.OverrideMetadata(typeof(FlaskProgressBar),
            new FrameworkPropertyMetadata(0D, (d, e) => SetValue((FlaskProgressBar)d, (double)e.NewValue)));
    }

    private RectangleGeometry? _content;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        _content = this.GetTemplateChild("PART_Content") as RectangleGeometry;
        SetValue(this, Value);
    }

    private static void SetValue(FlaskProgressBar instance, double value)
    {
        if (instance._content is not null)
        {
            var rect = instance._content.Rect;
            instance._content.Rect = new Rect(rect.X, rect.Height - value, rect.Width, rect.Height);
        }
    }
}

This ProgresBar looks like this:

enter image description here

The actual design of PathGeometry for the Path of outline is all up to you.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1