WPF
ProgressBar
TextOverlay
UI Design
C#

Text on a ProgressBar in WPF

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

WPF's ProgressBar control is good at showing progress visually, but it does not have a built-in text property for overlay content. In real applications you often want both: the bar itself and text such as 42%, Downloading, or 3 of 8 files. The normal solution is to compose controls rather than trying to force ProgressBar to render text on its own.

Why Overlaying Works Well in WPF

WPF layout makes it easy to stack controls in the same visual area. A Grid is the simplest approach because child elements share the same cell unless you place them elsewhere. That means you can put a ProgressBar in the background and a centered TextBlock on top.

This keeps responsibilities clear:

  • 'ProgressBar shows progress value'
  • 'TextBlock shows readable status'
  • bindings keep both in sync

That is usually better than editing the control template unless you need a fully reusable custom style.

A Basic XAML Overlay

Here is the simplest version:

xml
1<Grid Width="300" Height="26">
2    <ProgressBar x:Name="DownloadBar"
3                 Minimum="0"
4                 Maximum="100"
5                 Value="65" />
6
7    <TextBlock Text="65%"
8               HorizontalAlignment="Center"
9               VerticalAlignment="Center"
10               FontWeight="SemiBold"
11               Foreground="Black" />
12</Grid>

This is enough for fixed text, but most real screens need dynamic values. WPF binding makes that straightforward.

Binding the Text to the Same Value

A clean pattern is to expose progress state from a view model and bind both controls to it.

csharp
1using System.ComponentModel;
2using System.Runtime.CompilerServices;
3
4public class DownloadViewModel : INotifyPropertyChanged
5{
6    private double _progress;
7
8    public double Progress
9    {
10        get => _progress;
11        set
12        {
13            if (_progress != value)
14            {
15                _progress = value;
16                OnPropertyChanged();
17                OnPropertyChanged(nameof(ProgressText));
18            }
19        }
20    }
21
22    public string ProgressText => $"{Progress:0}%";
23
24    public event PropertyChangedEventHandler? PropertyChanged;
25
26    private void OnPropertyChanged([CallerMemberName] string? name = null)
27    {
28        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
29    }
30}

Bind that model in XAML:

xml
1<Grid Width="300" Height="26">
2    <ProgressBar Minimum="0"
3                 Maximum="100"
4                 Value="{Binding Progress}" />
5
6    <TextBlock Text="{Binding ProgressText}"
7               HorizontalAlignment="Center"
8               VerticalAlignment="Center"
9               FontWeight="Bold"
10               Foreground="Black" />
11</Grid>

This is a solid default because the text formatting stays in one place and the view remains simple.

Showing Richer Status Than a Percentage

Many applications need more than a percentage. You may want to display file counts, transfer speed, or a short status string. In that case, do not derive everything from the progress bar value. Store a dedicated status property.

csharp
public string Status => $"{CompletedFiles} of {TotalFiles} files";

Then bind the TextBlock to Status instead of ProgressText. The bar and the text can still update together, but they no longer pretend to represent the same kind of data.

When to Use a Control Template

If one screen needs overlaid text, the Grid approach is enough. If the same pattern appears throughout the application, a custom style or user control is better because it removes duplicate XAML.

A reusable user control can expose:

  • 'Value'
  • 'Minimum'
  • 'Maximum'
  • 'OverlayText'

That gives you a single component with a clean API while still relying on ordinary composition internally.

Visual Concerns

Text readability can degrade when the filled portion of the bar uses a dark color. A few practical fixes help:

  • use a bold font weight
  • keep the bar height tall enough for readable text
  • choose a foreground color with strong contrast
  • reduce visual clutter around the progress area

If the bar fill color changes dynamically, consider switching the text color based on theme or background contrast.

Indeterminate Progress

For indeterminate operations, the bar does not represent an exact numeric value, so percentage text becomes misleading. In that case, overlay status text such as Loading... or Connecting... and set IsIndeterminate="True".

xml
1<Grid Width="300" Height="26">
2    <ProgressBar IsIndeterminate="True" />
3    <TextBlock Text="Loading..."
4               HorizontalAlignment="Center"
5               VerticalAlignment="Center"
6               FontWeight="SemiBold" />
7</Grid>

This is a good reminder that the text is not just decoration. It communicates the meaning of the progress indicator.

Common Pitfalls

A common mistake is putting text inside a ProgressBar and expecting the control to render it. ProgressBar is not a content control, so that approach does not work.

Another issue is hardcoding the overlay text while the progress value changes. That leaves the user looking at stale information.

Be careful with alignment and margins. If the text is not centered in the same container as the bar, resizing can make the overlay drift.

Finally, avoid overcomplicating the solution with a custom template unless the project genuinely needs reuse or custom theming. For one screen, a Grid with bindings is easier to maintain.

Summary

  • WPF ProgressBar does not natively render overlay text.
  • The simplest solution is to stack a TextBlock on top of it in a Grid.
  • Bind the bar value and the overlay text to the same view model when they represent the same state.
  • Use a dedicated status property when the text is not just a percentage.
  • Prefer composition first and move to a reusable control only when repetition justifies it.
  • For indeterminate progress, use descriptive status text instead of fake numeric percentages.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.