如何模糊控件后面的背景

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 的按钮下方添加一层。

以下示例扩展了一个名为 BlurHostContentControl,它在 Border 元素之上呈现 Content,例如 Button实际上使用 VisualBrush.
模糊背景 画笔本身定义了一个图块,它位于 BlurHost 的位置,它承载 Button(或任何其他透明控件)。

实现模糊背景的基本步骤:

  1. 添加背景图片
  2. 在元素下方创建一个模糊层
  3. 获取元素的边界,例如相对于图像父级(最好是根容器)定位的 Button
  4. 使用边界矩形定义 VisualBrush 的图块(图像的实际部分)
  5. 在模糊层上应用画笔

使用示例

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
默认的StyleBlurHostGeneric.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>