So we created a glare effect on standard border in previous post but that insipid, boring border with rounded corners looked terrible. In this post, we’ll try to improve the shape, keeping the same old glaring effect we created.
We can’t change the shape of a border in WPF but we can use some other elements instead of border to achieve the same effect, like Path.
What we will do is to create different layers of Path’s and place the content above it ( instead of inside, like we do with Border ). Then maybe add some triggers ( or VisualStates ) for MouseOver, Selected etc states.
Let’s start with a simple path first;
Data=“M0,5 5,0 350,0 360,10 360,200 340,220 10,220 0,210 Z”
Opacity=“1”
Stroke=“#317185”
StrokeThickness=“2” />
As you can see from this simple declaration; Path is pretty much Border with a custom shape ( for this kind of usage ). Only non-trivial thing is the Data attribute and syntax for data input. I will not go into detail for that but you can read a lot more about XAML Geometry and syntax here. But to give you a tldr; version, it’s like an array of points; 0,5 –>5,0 –>350,0 etc.
And this is what it should look like;
Not bad, not bad at all…
And another trick before the real thing; using Path as Opacity Mask. Considering our rectangular image won’t fit in this irregular shaped frame.
<Image HorizontalAlignment=“Center”
VerticalAlignment=“Center”
SnapsToDevicePixels=“True”
Source=”{Binding Screenshot}“
Stretch=“UniformToFill”/>
<Border.OpacityMask>
<VisualBrush>
<VisualBrush.Visual>
<Path Data=“M2,5 5,2 350,2 358,10 358,200 340,218 10,218 2,210 Z” Fill=“White” />
</VisualBrush.Visual>
</VisualBrush>
</Border.OpacityMask>
</Border>
Putting image into a border is a good old trick to properly centralize stretch image ( UniformToFill ) which I use frequently. And then we use a Path as Visual Brush to create an Opacity Mask for that border.
Now let’s start our picture frame. I’ll use a grid as container for all path’s, images, triggers and other stuff. You can also use any other panel for this, like Canvas. Grid is just easy-mode.
<Border Width=“355” Height=“215”>
<Image HorizontalAlignment=“Center”
VerticalAlignment=“Center”
SnapsToDevicePixels=“True”
Source=”{Binding Screenshot}“
Stretch=“UniformToFill” />
<Border.OpacityMask>
<VisualBrush>
<VisualBrush.Visual>
<Path Data=“M2,5 5,2 350,2 358,10 358,200 340,218 10,218 2,210 Z” Fill=“White” />
</VisualBrush.Visual>
</VisualBrush>
</Border.OpacityMask>
</Border>
<Path Name=“MainBorder”
Data=”{StaticResource path}“
Opacity=“1”
Stroke=“#317185”
StrokeThickness=“2” />
<Path Name=“Background”
Data=”{StaticResource path}“
Opacity=“0.1”
StrokeThickness=“1”>
<Path.Fill>
<RadialGradientBrush Center=“0.5,0.5”>
<GradientStop Offset=“0” Color=“Transparent” />
<GradientStop Offset=“1” Color=“#148cd1” />
</RadialGradientBrush>
</Path.Fill>
</Path>
<Path Name=“BackgroundUpper”
Data=“M0,150 0,5 5,0 220,0 180,30 50,30 30,50 30,120 Z”
Opacity=“0.9”
Stroke=“#148cd1”
StrokeThickness=“1”>
<Path.Fill>
<LinearGradientBrush StartPoint=“0.5,0” EndPoint=“0.5,1”>
<GradientStop Offset=“0” Color=“#357194” />
<GradientStop Offset=“1” Color=“#045482” />
</LinearGradientBrush>
</Path.Fill>
</Path>
<Path Name=“BackgroundLower”
Data=“M360,60 340,90 340,170 310,200 160,200 130,220 340,220 360,200”
Opacity=“0.8”
Stroke=“#148cd1”
StrokeThickness=“1”>
<Path.Fill>
<LinearGradientBrush StartPoint=“0.5,0” EndPoint=“0.5,1”>
<GradientStop Offset=“0” Color=“#357194” />
<GradientStop Offset=“1” Color=“#045482” />
</LinearGradientBrush>
</Path.Fill>
</Path>
<Path Name=“Border”
Data=”{StaticResource path}“
StrokeThickness=“5”>
<Path.Stroke>
<LinearGradientBrush Opacity=“0.8” StartPoint=“0,0” EndPoint=“1,1”>
<GradientStop x:Name=“gs1” Offset=“-0.5” Color=“Transparent” />
<GradientStop x:Name=“gs2” Offset=“-0.4” Color=“Transparent” />
<GradientStop x:Name=“gs3” Offset=“0” Color=“#31C7F5” />
<GradientStop x:Name=“gs4” Offset=“0.4” Color=“Transparent” />
<GradientStop x:Name=“gs5” Offset=“1.5” Color=“Transparent” />
</LinearGradientBrush>
</Path.Stroke>
</Path>
<Grid.Triggers>
<EventTrigger RoutedEvent=“Grid.Loaded”>
<BeginStoryboard Name=“Glance”>
<Storyboard RepeatBehavior=“Forever”>
<DoubleAnimation Duration=“0:0:5”
From=“-0.1”
Storyboard.TargetName=“gs2”
Storyboard.TargetProperty=“Offset”
To=“5.0” />
<DoubleAnimation Duration=“0:0:5”
From=“0”
Storyboard.TargetName=“gs3”
Storyboard.TargetProperty=“Offset”
To=“5.1” />
<DoubleAnimation Duration=“0:0:5”
From=“0.1”
Storyboard.TargetName=“gs4”
Storyboard.TargetProperty=“Offset”
To=“5.2” />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>
OK this is a lot of stuff, so before explaining what’s going on here, I’ll show you the result;
It might look a bit complex for a simple starter sample but it’s rather easy really.
Now let’s go back and have a look at stuff inside our container grid. First of all, we have our image at the bottom of everything ( wrapped by a border but it’s not really important at this point ). The wrapper border also uses a path as Opacity Mask to crop the sharp corners of our rectangular image to fit.
After that, there is “MainBorder”, the non-animated static thin line you see around our image. Nothing fancy here. It uses a StaticResource path, to show it’s not only possible but also much better option than writing Path data again and again.
Then there is the “Background” layer. I guess it’s a wrong name for it though as it’s above the image, not behind. It’ creates a blue haze around the image, using a RadialGradientBrush. You may remove it as well, but I think it helps to mesh the colorful image with our blue sci-fi frame.
Above that, there is “BackgroundUpper” and “BackgroundLower” . I don’t even know why I keep them calling “Background” even though they are on top of everything but whatever. Those are that plate-like parts at the top-left and bottom-right of the image. Kinda helps the mood I guess. They both use LinearGradientBrush as SolidColorBrush felt & looked boring.
Then there is “Border”, pretty much the path version of the border we created in previous post. This time it doesn’t have the static line though ( we use MainBorder for that ). This one is mainly transparent and there is just a thick glare which looks like it’s moving on MainBorder as they stacked perfectly. Kind of hard to describe but you’ll know what I mean as soon as you see it.
I believe the Grid Triggers are exactly same as the Border Triggers we created in previous post. They just change the Offset of LinearGradientBrush that we use in “Border”. The animation is on a 5 second loop, first 1 second is the glare effect moving through the border and next 4 seconds is just a padding between animations.
Aaand that’s it. I guess just “MainBorder” and “Border” would be enough but I wanted to show some extra stuff you can do. Playing around with XAML is always fun but overdoing it hurts it more than helps.
Ah and one more thing, this won’t work in Silverlight as it is. Silverlight doesn’t have VisualBrush ( which we used for Image OpacityMask ) and some other stuff. I bet there are equivalent stuff or a work around but that’s beyond me.
I hope you liked it, see you next time!