带有图像和填充效果的 WPF 进度条

WPF Progressbar with images and fill effect

我想创建自定义进度条样式,以显示从底部填充的图像。 我创建了两个图像,背景:

和前景:

想法是创建这样的东西:

我在 Blend 中创建了这种样式:

<Style x:Key="ImageFill" TargetType="{x:Type ProgressBar}">
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ProgressBar}">
                <Grid x:Name="TemplateRoot" SnapsToDevicePixels="true">
                    <Image x:Name="PART_Track" Source="Play_Background.png" Margin="1" Stretch="Fill"/>
                    <Rectangle x:Name="PART_Indicator" Margin="1" HorizontalAlignment="Left" Fill="#FFD6931C">
                        <Rectangle.OpacityMask>
                                    <RadialGradientBrush>
                                <GradientStop Color="Black" Offset="0.87"/>
                                <GradientStop Color="Transparent" Offset="0.87"/>
                            </RadialGradientBrush>
                        </Rectangle.OpacityMask>
                    </Rectangle>
                    <Image Source="Play_Foreground.png" Margin="1" Stretch="Fill"/>
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

但是当将值设置为 60 之类的值时,我得到了这个:

我可以将 OpacitMmask 更改为:

<RadialGradientBrush Center="106,104" GradientOrigin="60,60" MappingMode="Absolute" RadiusY="97" RadiusX="98">
    <GradientStop Color="Black" Offset="0.87"/>
    <GradientStop Color="Transparent" Offset="0.87"/>
</RadialGradientBrush>

但是当我调整进度条大小时我得到了不想要的行为:

如何解决这个问题?我需要掩码将 MappingMode 设置为 RelativeToBoundingBox,这样我就可以为进度条设置不同的大小。

下面是完整的 XAML 我在 Blend 中生成:

<Window x:Class="ImageProgressBar.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="389" Width="523">
    <Window.Resources>
<Style x:Key="ImageFill" TargetType="{x:Type ProgressBar}">
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ProgressBar}">
                <Grid x:Name="TemplateRoot" SnapsToDevicePixels="true">
                    <Image x:Name="PART_Track" Source="Play_Background.png" Margin="1" Stretch="Fill"/>
                    <Rectangle x:Name="PART_Indicator" Margin="1" HorizontalAlignment="Left" Fill="#FFD6931C">
                        <Rectangle.OpacityMask>
                            <RadialGradientBrush Center="106,104" GradientOrigin="60,60" MappingMode="Absolute" RadiusY="97" RadiusX="98">
                                <GradientStop Color="Black" Offset="0.87"/>
                                <GradientStop Color="Transparent" Offset="0.87"/>
                            </RadialGradientBrush>
                        </Rectangle.OpacityMask>
                    </Rectangle>
                    <Image Source="Play_Foreground.png" Margin="1" Stretch="Fill"/>
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
    </Window.Resources>
    <Grid>
        <ProgressBar 
        Maximum="{Binding ElementName=MySlider, Path=Maximum, Mode=TwoWay}" Minimum="{Binding ElementName=MySlider, Path=Minimum, Mode=TwoWay}" Value="{Binding ElementName=MySlider, Path=Value, Mode=TwoWay}"
        HorizontalAlignment="Left" Height="200" Margin="10,10,0,0" VerticalAlignment="Top" Width="200" Style="{DynamicResource ImageFill}"/>

                <ProgressBar 
        Maximum="{Binding ElementName=MySlider, Path=Maximum, Mode=TwoWay}" Minimum="{Binding ElementName=MySlider, Path=Minimum, Mode=TwoWay}" Value="{Binding ElementName=MySlider, Path=Value, Mode=TwoWay}"
        HorizontalAlignment="Left" Height="100" Margin="294,10,0,0" VerticalAlignment="Top" Width="100" Style="{DynamicResource ImageFill}"/>

        <Slider  Name="MySlider" HorizontalAlignment="Left" Margin="10,215,0,0" VerticalAlignment="Top" Width="200" Minimum="0" Maximum="100" Value="0"/>

    </Grid>
</Window>

我找到了 http://vbcity.com/blogs/xtab/archive/2009/11/24/wpf-controltemplates-creating-a-non-rectangular-progressbar.aspx 但不能在我的风格中使用它。

检查一下 WPF: Anchor points don't work well sometimes 问题是您的大小和边距是绝对的,而不是相对于您的控件的大小。

我已经成功地创建了我想要的效果的样式。 下面是它的样子:

以下是使用样式和滑块更改进度条值的工作代码:

<?xml version="1.0" encoding="UTF-8"?>
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="ImageProgressBar.MainWindow" Title="ProgressBar Image Fill" Height="284" Width="598" Background="#FFEAE0E0">
   <Window.Resources>
      <Style x:Key="ImageFill" TargetType="{x:Type ProgressBar}">
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="{x:Type ProgressBar}">
                  <Grid x:Name="TemplateRoot" SnapsToDevicePixels="true">
                     <Image x:Name="PART_Track" Source="Play_Background.png" Margin="1" Stretch="Fill" />
                     <Rectangle x:Name="PART_Indicator" Margin="1" HorizontalAlignment="Left" Fill="{TemplateBinding Foreground}">
                        <Rectangle.OpacityMask>
                           <RadialGradientBrush Center="106,104" GradientOrigin="60,60" MappingMode="Absolute" RadiusY="97" RadiusX="98">
                              <GradientStop Color="Black" Offset="0.87" />
                              <GradientStop Color="Transparent" Offset="0.87" />
                           </RadialGradientBrush>
                        </Rectangle.OpacityMask>
                     </Rectangle>
                     <Image Source="Play_Foreground.png" Margin="1" Stretch="Fill" />
                  </Grid>
                  <ControlTemplate.Triggers>
                     <!-- Getting vertical style working using technique described here:  -->
                     <Trigger Property="Orientation" Value="Vertical">
                        <Setter TargetName="PART_Indicator" Property="LayoutTransform">
                           <Setter.Value>
                              <RotateTransform Angle="270" />
                           </Setter.Value>
                        </Setter>
                        <Setter TargetName="PART_Indicator" Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height}" />
                        <Setter TargetName="PART_Indicator" Property="Height" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}" />
                        <Setter TargetName="PART_Indicator" Property="VerticalAlignment" Value="Bottom" />
                     </Trigger>
                  </ControlTemplate.Triggers>
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
      <Style x:Key="SimpleImageFill" TargetType="{x:Type ProgressBar}">
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="{x:Type ProgressBar}">
                  <Grid x:Name="TemplateRoot" SnapsToDevicePixels="true">
                     <Image x:Name="PART_Track" Source="Play_Game_Empty.png" />
                     <Canvas ClipToBounds="True" x:Name="PART_Indicator" HorizontalAlignment="Left">
                        <Image x:Name="Image_Fill" Source="Play_Game_Fill.png"
                        Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}"
                        Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height}" />
                     </Canvas>
                  </Grid>
                  <ControlTemplate.Triggers>
                     <Trigger Property="Orientation" Value="Vertical">
                        <Setter TargetName="PART_Indicator" Property="LayoutTransform">
                           <Setter.Value>
                              <RotateTransform Angle="270" />
                           </Setter.Value>
                        </Setter>
                        <Setter TargetName="Image_Fill" Property="LayoutTransform">
                           <Setter.Value>
                              <RotateTransform Angle="-270" />
                           </Setter.Value>
                        </Setter>
                        <Setter TargetName="PART_Indicator" Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height}" />
                        <Setter TargetName="PART_Indicator" Property="Height" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}" />
                        <Setter TargetName="PART_Indicator" Property="VerticalAlignment" Value="Bottom" />
                     </Trigger>
                  </ControlTemplate.Triggers>
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
   </Window.Resources>
   <Grid>
      <ProgressBar Foreground="Orange" Orientation="Vertical" Maximum="{Binding ElementName=MySlider, Path=Maximum, Mode=TwoWay}" Minimum="{Binding ElementName=MySlider, Path=Minimum, Mode=TwoWay}" Value="{Binding ElementName=MySlider, Path=Value, Mode=TwoWay}" HorizontalAlignment="Left" Height="200" Margin="10,10,0,0" VerticalAlignment="Top" Width="200" Style="{DynamicResource ImageFill}" />
      <ProgressBar Maximum="{Binding ElementName=MySlider, Path=Maximum, Mode=TwoWay}" Minimum="{Binding ElementName=MySlider, Path=Minimum, Mode=TwoWay}" Value="{Binding ElementName=MySlider, Path=Value, Mode=TwoWay}" HorizontalAlignment="Left" Height="200" Margin="227.5,10,0,0" VerticalAlignment="Top" Width="200" Style="{DynamicResource ImageFill}" />
      <Slider Name="MySlider" HorizontalAlignment="Left" Margin="10,215,0,0" VerticalAlignment="Top" Width="200" Minimum="0" Maximum="100" Value="0" />
      <ProgressBar Orientation="Vertical" Maximum="{Binding ElementName=MySlider, Path=Maximum, Mode=TwoWay}" Minimum="{Binding ElementName=MySlider, Path=Minimum, Mode=TwoWay}" Value="{Binding ElementName=MySlider, Path=Value, Mode=TwoWay}" HorizontalAlignment="Left" Height="150" Margin="432.5,10,0,0" VerticalAlignment="Top" Width="150" Style="{DynamicResource SimpleImageFill}" />
   </Grid>
</Window>

我实际上创建了两种样式:

  1. 第一个是使用两张图片(我问题中的前两张)和圆圈 它们之间。想法是从底部或左侧填充圆圈。 圆圈用前景色填充。
  2. 第二个要简单得多,它只使用两个图像(空的和填充的)。当值改变时,填充图像,即在空的顶部,从左或从底部显示。我使用了充满图像的 canvas,我改变了它的宽度或高度。

我刚开始使用 WPF,请问有人知道更好的解决方案吗post回答。

下面我添加了应用于第三个进度条的第二种样式使用的两个图像。