简体   繁体   中英

How to display text on a progress bar? [C# WPF]

I need to display a sentence on a progress bar, I've found XAML solutions but I need C# specific solutions, meaning I need to set the text in code.

This is an image of what I'm trying to do, in a nutshell

The way I have done this is just create a Grid and place the ProgressBar and TextBlock in the same cell of the grid so the text is overlayed.

<Grid Height="30" Margin="5">
            <ProgressBar Grid.Row="0"/>
            <TextBlock Text="Your Text" VerticalAlignment="Center" Grid.Row="0" HorizontalAlignment="Center"/>
        </Grid>

By placing text on the progress control you are effectively adding an extra property that needs to be set, so whichever solution you choose will need to provide that property somehow. In short, there are 3 main ways of doing this:

  1. Create a custom control.
  2. Template the control, using the 'Tag' property to set the text.
  3. Template the control, using an attached property to set the text.

#1 is very old-school WinForms-like, and a bit heavy-handed for what you're trying to do. #2 is probably the easiest, but ties up the Tag property, which you or your users may need to use for something else in future. #3, while being a bit more involved, is the "proper" WPF way to do this, and the one I'll show here.

First you'll need to create an attached property that you'll use to set your text, something like this will do fine:

internal class AttachedProperties
{
    public static string GetMyText(DependencyObject obj)
    {
        return (string)obj.GetValue(MyTextProperty);
    }

    public static void SetMyText(DependencyObject obj, string value)
    {
        obj.SetValue(MyTextProperty, value);
    }

    public static readonly DependencyProperty MyTextProperty =
        DependencyProperty.RegisterAttached("MyText", typeof(String), typeof(AttachedProperties), new PropertyMetadata(""));
}

Next you need to template the ProgressBar control. Start by declaring a ProgressBar in XAML:

<ProgressBar Width="200" Height="50" Minimum="0" Maximum="100" Value="50" />

Now select the control in the XAML visual editor, and in the Properties panel in the bottom-right click on the little box (or drop down menu in VS2022) to the right of "Template" and select "Convert to new resource". Give it a name (eg "CustomProgressBarTemplate") and click Ok.

What this has now done is expanded the default progress bar out to a template resource and changed your control to use it. Your ProgressBar tag now looks like this:

<ProgressBar Template="{DynamicResource CustomProgressBarTemplate}" Width="200" Height="50" Minimum="0" Maximum="100" Value="50" />

The template itself has a parent grid with all the visual elements on it, so all you need to do is add a TextBlock that binds to your attached property ie something like this as the last element of the Grid:

<TextBlock Text="{TemplateBinding local:AttachedProperties.MyText}" FontSize="24" Foreground="Black" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0" />

So the final full ControlTemplate definition should wind up looking like this:

<ControlTemplate x:Key="CustomProgressBarTemplate" TargetType="{x:Type ProgressBar}">
    <Grid x:Name="TemplateRoot">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Determinate"/>
                <VisualState x:Name="Indeterminate">
                    <Storyboard RepeatBehavior="Forever">
                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="Animation" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                            <EasingDoubleKeyFrame KeyTime="0" Value="0.25"/>
                            <EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.25"/>
                            <EasingDoubleKeyFrame KeyTime="0:0:2" Value="0.25"/>
                        </DoubleAnimationUsingKeyFrames>
                        <PointAnimationUsingKeyFrames Storyboard.TargetName="Animation" Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)">
                            <EasingPointKeyFrame KeyTime="0" Value="-0.5,0.5"/>
                            <EasingPointKeyFrame KeyTime="0:0:1" Value="0.5,0.5"/>
                            <EasingPointKeyFrame KeyTime="0:0:2" Value="1.5,0.5"/>
                        </PointAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
        <Rectangle x:Name="PART_Track"/>
        <Grid x:Name="PART_Indicator" ClipToBounds="true" HorizontalAlignment="Left">
            <Rectangle x:Name="Indicator" Fill="{TemplateBinding Foreground}"/>
            <Rectangle x:Name="Animation" Fill="{TemplateBinding Foreground}" RenderTransformOrigin="0.5,0.5">
                <Rectangle.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform/>
                        <TranslateTransform/>
                    </TransformGroup>
                </Rectangle.RenderTransform>
            </Rectangle>
        </Grid>
        <TextBlock Text="{TemplateBinding local:AttachedProperties.MyText}" FontSize="24" Foreground="Black" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0" />
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="Orientation" Value="Vertical">
            <Setter Property="LayoutTransform" TargetName="TemplateRoot">
                <Setter.Value>
                    <RotateTransform Angle="-90"/>
                </Setter.Value>
            </Setter>
        </Trigger>
        <Trigger Property="IsIndeterminate" Value="true">
            <Setter Property="Visibility" TargetName="Indicator" Value="Collapsed"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

So now you can either set the text in XAML like this:

<ProgressBar local:AttachedProperties.MyText="Hello World!" Template="{DynamicResource CustomProgressBarTemplate}" Width="200" Height="50" Minimum="0" Maximum="100" Value="50" />

Or you can give your ProgressBar a name and set it directly in code, which is the crux of your question:

myProgressBar.SetValue(AttachedProperties.MyTextProperty, "Hello World!");

Result:

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM