【问题标题】:WPF Progressbar with images and fill effect带有图像和填充效果的 WPF 进度条
【发布时间】:2015-09-25 20:29:53
【问题描述】:

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

和前景:

想法是创建这样的东西:

在 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,这样我就可以为进度条设置不同的大小。

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

<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,但不能在我的风格中使用它。

【问题讨论】:

  • 看起来您并没有将所有内容都剪辑在一起。因此,图像某些部分的宽度和高度不依赖于其他部分。这就是为什么当您调整大小时它们无法正确调整大小
  • @rmn36 如何将所有内容剪辑在一起?
  • @rmn36 任何提示如何以该样式将所有内容剪辑在一起?

标签: c# wpf .net-4.5


【解决方案1】:

我已经设法创造出我想要的效果。 下面是它的外观:

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

<?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: http://stackoverflow.com/a/6849237/7532 -->
                     <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. 第二个更简单,它只使用两个图像(空的和填充的)。当值更改填充图像时,位于空白顶部的图像从左侧或底部显示。我使用了充满图像的画布,我更改了它的宽度或高度。

我刚开始使用 WPF,所以有人知道更好的解决方案,请发布答案。

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我通过默认样式的副本创建了这种样式。图像缩放正确,但我无法获得圆形填充效果。更改RadialGradientBrush MappingMode="Absolute" 后,我得到了这种效果,但是调整进度条的大小会阻止一切。我该如何解决这个问题?
    猜你喜欢
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    • 2021-02-19
    • 1970-01-01
    • 1970-01-01
    • 2019-07-11
    • 2017-12-14
    • 1970-01-01
    相关资源
    最近更新 更多