如何模糊控件后面的背景
How to blur the background behind a control
我需要使按钮中使用的 VisualBrush 适合整个 window。 VisualBrush 链接到拉伸到整个可视化效果的图像,但在视觉效果中该图像开始出现在按钮的一角。
<Button x:Name="button" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Acquista ora- .99" FontSize="48" BorderBrush="{x:Null}">
<Button.Background>
<VisualBrush Visual="{Binding ElementName=img}" Stretch="None" AlignmentX="Center" AlignmentY="Center" ViewboxUnits="RelativeToBoundingBox" ViewportUnits="RelativeToBoundingBox" />
</Button.Background>
</Button>
我该怎么办?提前致谢。
如果想模糊 Button
(或一般的透明控件)后面的图像,您必须采用不同的方法。
您需要图像的确切图块才能使用 BlurEffect
.
对其进行模糊处理
为了不模糊 Button
本身,您必须在应用了 BlurEffect
的按钮下方添加一层。
以下示例扩展了一个名为 BlurHost
的 ContentControl
,它在 Border
元素之上呈现 Content
,例如 Button
实际上使用 VisualBrush
.
模糊背景
画笔本身定义了一个图块,它位于 BlurHost
的位置,它承载 Button
(或任何其他透明控件)。
实现模糊背景的基本步骤:
- 添加背景图片
- 在元素下方创建一个模糊层
- 获取元素的边界,例如相对于图像父级(最好是根容器)定位的 Button
- 使用边界矩形定义 VisualBrush 的图块(图像的实际部分)
- 在模糊层上应用画笔
使用示例
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- .99" />
</local:BlurHost>
</Grid>
</Window>
实现示例
实现很简单。您必须添加 属性 更改的处理程序才能使控件动态化。
BlurHost.cs
ContentControl
用作容器。在内容的透明区域可以看到模糊的背景。
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
默认的Style
为BlurHost
。 Generic.xaml 文件位于应用程序(项目)的 Themes 文件夹中。
<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>
我需要使按钮中使用的 VisualBrush 适合整个 window。 VisualBrush 链接到拉伸到整个可视化效果的图像,但在视觉效果中该图像开始出现在按钮的一角。
<Button x:Name="button" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Acquista ora- .99" FontSize="48" BorderBrush="{x:Null}">
<Button.Background>
<VisualBrush Visual="{Binding ElementName=img}" Stretch="None" AlignmentX="Center" AlignmentY="Center" ViewboxUnits="RelativeToBoundingBox" ViewportUnits="RelativeToBoundingBox" />
</Button.Background>
</Button>
我该怎么办?提前致谢。
如果想模糊 Button
(或一般的透明控件)后面的图像,您必须采用不同的方法。
您需要图像的确切图块才能使用 BlurEffect
.
为了不模糊 Button
本身,您必须在应用了 BlurEffect
的按钮下方添加一层。
以下示例扩展了一个名为 BlurHost
的 ContentControl
,它在 Border
元素之上呈现 Content
,例如 Button
实际上使用 VisualBrush
.
模糊背景
画笔本身定义了一个图块,它位于 BlurHost
的位置,它承载 Button
(或任何其他透明控件)。
实现模糊背景的基本步骤:
- 添加背景图片
- 在元素下方创建一个模糊层
- 获取元素的边界,例如相对于图像父级(最好是根容器)定位的 Button
- 使用边界矩形定义 VisualBrush 的图块(图像的实际部分)
- 在模糊层上应用画笔
使用示例
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- .99" />
</local:BlurHost>
</Grid>
</Window>
实现示例
实现很简单。您必须添加 属性 更改的处理程序才能使控件动态化。
BlurHost.cs
ContentControl
用作容器。在内容的透明区域可以看到模糊的背景。
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
默认的Style
为BlurHost
。 Generic.xaml 文件位于应用程序(项目)的 Themes 文件夹中。
<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>