Reputation: 4919
I'd like to create custom progressbar style that will display image filling up from bottom. I've created two images, background:
and foreground:
Idea is to create something like this:
Inside Blend I've created this style:
<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>
But when setting value to something like 60 I get this:
I can change OpacitMmask to this:
<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>
but then when I resize my progress bar I get unwanted behaviour:
How this can be fixed? I need mask to have MappingMode set to RelativeToBoundingBox so I can set different size to progressbar.
Below is full XAML I've generated in 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>
I've found http://vbcity.com/blogs/xtab/archive/2009/11/24/wpf-controltemplates-creating-a-non-rectangular-progressbar.aspx but can't use this inside my style.
Upvotes: 2
Views: 2236
Reputation: 4919
I've managed to create style that has effect I wanted. Below is how it looks:
and below is working code with style and slider to change value of progressbar:
<?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>
I've actually created two styles:
I'm just starting with WPF, so is someone knows a better solution please post answer.
Below I'm adding two images used by second style applied to third progressbar.
Upvotes: 8
Reputation: 656
Check this out WPF: Anchor points don't work well sometimes The problem is that your sizing and margins are absolutes not relative the the size of your control.
Upvotes: 0