If want to blur the image behind the Button
(or a transparent control in general) you have to follow a different approach.
You need the exact tile of the image in order to blur it using the BlurEffect
.
In order to not blur the Button
itself, you must add alayer beneath the button that has the BlurEffect
applied.
The following example extends a ContentControl
named BlurHost
that renders the Content
e.g., the Button
, on top of a Border
element that will actualy blur the background using a VisualBrush
.
The brush itself has a tile defined that is located at the position of the BlurHost
which hosts the Button
(or any other transparent control).
The basic steps to implement a blurred background:
- Add the background image
- Create a blur layer beneath the element
- Get the bounds of the element e.g., the Button which is located relative to the parent of the Image (preferably the root container)
- Use the bounding rectangle to define the tile of the VisualBrush (the actual section of the image)
- Apply the brush on the blur layer
Usage example
MainWindow.xaml
<Window>
<!-- Allow the root grid to stretch accross the Window -->
<Grid>
<Image x:Name="img" Source="/someImage.png" />
<!--
Optionally override the default BlurEffect
by setting the BlurHost.BlurEffect property
-->
<local:BlurHost BlurBackground="{Binding ElementName=img}"
BlurOpacity="1"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Button Background="Transparent"
FontSize="48"
Content="Acquista ora- $23.99" />
</local:BlurHost>
</Grid>
</Window>

Implementation example
The implementation is simple. You have to add property changed handlers in order to make the control dynamic.
BlurHost.cs
The ContentControl
serves as a container. The blurred background is visible at the transparent areas of the content.
public class BlurHost : ContentControl
{
public Visual BlurBackground
{
get => (Visual)GetValue(BlurBackgroundProperty);
set => SetValue(BlurBackgroundProperty, value);
}
public static readonly DependencyProperty BlurBackgroundProperty =
DependencyProperty.Register(
"BlurBackground",
typeof(Visual),
typeof(BlurHost),
new PropertyMetadata(default(Visual), OnBlurBackgroundChanged));
public double BlurOpacity
{
get => (double)GetValue(BlurOpacityProperty);
set => SetValue(BlurOpacityProperty, value);
}
public static readonly DependencyProperty BlurOpacityProperty =
DependencyProperty.Register(
"BlurOpacity",
typeof(double),
typeof(BlurHost),
new PropertyMetadata(1.0));
public BlurEffect BlurEffect
{
get => (BlurEffect)GetValue(BlurEffectProperty);
set => SetValue(BlurEffectProperty, value);
}
public static readonly DependencyProperty BlurEffectProperty =
DependencyProperty.Register(
"BlurEffect",
typeof(BlurEffect),
typeof(BlurHost),
new PropertyMetadata(
new BlurEffect()
{
Radius = 10,
KernelType = KernelType.Gaussian,
RenderingBias = RenderingBias.Performance
}));
private Border PART_BlurDecorator { get; set; }
private VisualBrush BlurDecoratorBrush { get; set; }
static BlurHost()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(BlurHost), new FrameworkPropertyMetadata(typeof(BlurHost)));
}
public BlurHost()
{
Loaded += OnLoaded;
// TODO::Update Opacity of VisualBrush when property BlurOpacity changes
this.BlurDecoratorBrush = new VisualBrush()
{
ViewboxUnits = BrushMappingMode.Absolute,
Opacity = this.BlurOpacity
};
}
private void DrawBlurredElementBackground()
{
if (!TryFindVisualRootContainer(this, out FrameworkElement rootContainer))
{
return;
}
// Get the section of the image where the BlurHost element is located
Rect elementBounds = TransformToVisual(rootContainer)
.TransformBounds(new Rect(this.RenderSize));
// Use the section bounds to actually "cut out" the image tile
this.BlurDecoratorBrush.Viewbox = elementBounds;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (TryFindVisualRootContainer(this, out FrameworkElement rootContainer))
{
rootContainer.SizeChanged += OnRootContainerElementResized;
}
DrawBlurredElementBackground();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.PART_BlurDecorator = GetTemplateChild("PART_BlurDecorator") as Border;
this.PART_BlurDecorator.Effect = this.BlurEffect;
this.PART_BlurDecorator.Background = this.BlurDecoratorBrush;
}
private static void OnBlurBackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var this_ = d as BlurHost;
this_.BlurDecoratorBrush.Visual = e.NewValue as Visual;
this_.DrawBlurredElementBackground();
}
private void OnRootContainerElementResized(object sender, SizeChangedEventArgs e)
=> DrawBlurredElementBackground();
private bool TryFindVisualRootContainer(DependencyObject child, out FrameworkElement rootContainerElement)
{
rootContainerElement = null;
DependencyObject parent = VisualTreeHelper.GetParent(child);
if (parent == null)
{
return false;
}
if (parent is not Window visualRoot)
{
return TryFindVisualRootContainer(parent, out rootContainerElement);
}
rootContainerElement = visualRoot.Content as FrameworkElement;
return true;
}
}
Generic.xaml
The default Style
for the BlurHost
. The Generic.xaml file is located in the Themes folder of the application (project).
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Net.Wpf">
<Style TargetType="local:BlurHost">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:BlurHost">
<Grid>
<!-- Blur layer beneath the hosted element (ContentPresenter) -->
<Border x:Name="PART_BlurDecorator"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"/>
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>