Search code examples
c#labelwpf-controlsbackcolor

C# WPF Label background filled according to a percentage property


I would like to make a CustomControl Label, which could have a property called Percentage and the background would be filled according to that percentage. example: label.Percentage = 70 then the background color would fill only 70% of the label size.

example

I've created a Custom Control:

<UserControl x:Class="NameSpace.PercentageLabel"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:NameSpace"                
          Background="Transparent">


<Border x:Name="backgroundBorder" Background="Transparent">
    <TextBlock x:Name="textBlock" Text="Label Text" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
</Border>

I set the properties of the PercentageLabel like this:

 public partial class PercentageLabel : UserControl
 {

     public PercentageLabel()
     {
         InitializeComponent();
     }

     public static readonly DependencyProperty PercentageProperty =
       DependencyProperty.Register("Percentage", typeof(double), typeof(PercentageLabel), new PropertyMetadata(0.0, OnPercentageChanged));

     public double Percentage
     {
         get { return (double)GetValue(PercentageProperty); }
         set { SetValue(PercentageProperty, value); }
     }

     public static readonly DependencyProperty FillColorProperty =
         DependencyProperty.Register("FillColor", typeof(Brush), typeof(PercentageLabel), new PropertyMetadata(Brushes.Black));

 

     public Brush FillColor
     {
         get { return (Brush)GetValue(FillColorProperty); }
         set { SetValue(FillColorProperty, value); }
     }

     private static void OnPercentageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
         var label = (PercentageLabel)d;
         var newPercentage = (double)e.NewValue;

         // Ensure the percentage is within the valid range
         newPercentage = Math.Min(100, Math.Max(0, newPercentage));

         // Calculate the width of the filled and unfilled parts of the background
         var fillWidth = label.ActualWidth * (newPercentage / 100);          

         // Update the background color (FillColor) and the filled background width
         label.backgroundBorder.Background = label.FillColor;
         label.backgroundBorder.Width = fillWidth;
         label.backgroundBorder.HorizontalAlignment= HorizontalAlignment.Left;
     }
 }   

And finnaly I add the custom control in a Window.

<local:PercentageLabel Grid.Row="0" Grid.Column="0" Percentage="70" FillColor="Green"/>

Problems:

  • I can't edit the custom label Content otherwise the background disappears.
  • The label text takes the size of the percentage as well, therefore we can't see any text for low percentage values.

Solution

  • You could use Grid and overlapping Controls, textBlockBar is your percentage bar that will fill whatever you need

    textBlockBG is the background box that will have your text and background Sample

    In your user control:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"></RowDefinition>
        </Grid.RowDefinitions>
    
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        
        <Border x:Name="backgroundBorder1" Background="Transparent" Grid.Row="0" Grid.Column="0">
            <TextBlock x:Name="textBlockBar"  HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" Opacity="0.1" Text="">
            </TextBlock>
        </Border>
        <Border x:Name="backgroundBorder2" Background="Transparent" Grid.Row="0" Grid.Column="0">
            <TextBlock x:Name="textBlockBG"  HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" Opacity="1" Text="">
            </TextBlock>
        </Border>
    </Grid>
    

    Update your Usercontrol class accordingly:

    public partial class PercentageLabel : UserControl
    {
    
        public PercentageLabel()
        {
            InitializeComponent();
        }
    
        public static readonly DependencyProperty PercentageProperty =
          DependencyProperty.Register("Percentage", typeof(double), typeof(PercentageLabel), new PropertyMetadata(0.0, OnPercentageChanged));
    
        public double Percentage
        {
            get { return (double)GetValue(PercentageProperty); }
            set { SetValue(PercentageProperty, value); }
        }
    
        public static readonly DependencyProperty FillColorProperty =
            DependencyProperty.Register("FillColor", typeof(Brush), typeof(PercentageLabel), new PropertyMetadata(Brushes.Black));
    
        public string Text
        {
            get
            {
                return this.textBlockBG.Text;
            }
            set
            {
                this.textBlockBG.Text = value;
            }
        }
    
        public Brush FillColor
        {
            get { return (Brush)GetValue(FillColorProperty); }
            set { SetValue(FillColorProperty, value); }
        }
    
        public Brush BackColor
        {
            get { return this.Background; }
            set { this.Background = value; }
        }
    
        public Brush TextColor
        {
            get { return this.textBlockBG.Foreground; }
            set { this.textBlockBG.Foreground = value; }
        }
    
        private static void OnPercentageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var label = (PercentageLabel)d;
            var newPercentage = (double)e.NewValue;
    
            // Ensure the percentage is within the valid range
            newPercentage = Math.Min(100, Math.Max(0, newPercentage));
    
            // Calculate the width of the filled and unfilled parts of the background
            var fillWidth = label.ActualWidth * (newPercentage / 100);
    
            // Update the background color (FillColor) and the filled background width
            label.backgroundBorder1.Background = label.FillColor;
            label.backgroundBorder1.Width = fillWidth;
            label.backgroundBorder1.HorizontalAlignment = HorizontalAlignment.Left;
    
            
        }
    

    Then in your Window, just do this

    <local:PercentageLabel x:Name="pLabel1" Grid.Row="0" Grid.Column="0" Percentage="70" FillColor="Blue" BackColor="Orange" TextColor="Yellow"/>
    

    There is an issue though that you need to handle when you resize your window the % does not follow the window size, I will leave it to you to fix it :)