2

在 WPF 中,大多数控件都有一个Effect属性,例如Grid.Effect允许创建模糊网格。我想看看如何在 WinRT 中创建背景模糊的弹出窗口?

4

3 回答 3

2

XAML 中的 WinRT 不支持效果。您可以使用 DirectX 互操作并使用 C++ (SwapChainPanel) 将着色器放在那里。

于 2013-10-25T21:20:43.360 回答
2

正如您所注意到的,Effects 目前在 WinRT 中不可用。

想法:

  1. 我建议考虑不要创建模糊的背景效果,因为它不会在 Windows 应用商店应用程序中提供一致的体验。Effects,至少在 Widows 应用商店中的“设计语言”并不是一个很好的匹配。
  2. 或者,您可以只覆盖带有一些噪点的半透明图像,以至少给人一种不均匀的效果(而不仅仅是使部分透明的纯色)。
  3. 或者,也许只是将背景“变暗”(同样,叠加层会让这很容易)以提供更“灯箱”风格的效果。
  4. 虽然您可能能够使用互操作,但这可能会显着增加您的应用程序的复杂性,尤其是当它用于诸如此类的简单功能时。
于 2013-10-26T01:38:38.520 回答
2

不这样做或使用 DirectX 的替代方法可能是将新RenderTargetBitmap类与RenderAsync()允许您从 C# 或 VB 级别访问控件像素的方法一起使用(假设这是您正在使用的),这可能更容易比 DirectX 快,并且实际上足够快,可以在 CPU 上运行一些基本效果。我FxContentControl上周末在 WinRT XAML Toolkit 中完成了它,它似乎在一些简单的场景中工作正常,主要问题是 - 何时更新效果。

该控件当前不模糊任何内容,而是找到内容的扩展像素并将它们变为黑色,作为一个穷人的方式来为TextBlock具有一些透明和一些非透明区域的 a 或任何其他控件生成笔画。您可以将其更改为模糊或添加选项以相当容易地按需进行。

这是模板化控件的默认样式:

<Style
    TargetType="controls:FxContentControl">
    <Setter
        Property="HorizontalContentAlignment"
        Value="Left" />
    <Setter
        Property="VerticalContentAlignment"
        Value="Top" />
    <Setter
        Property="Template">
        <Setter.Value>
            <ControlTemplate
                TargetType="controls:FxContentControl">
                <Grid>
                    <Image
                        x:Name="BackgroundFxImage"
                        Stretch="None"
                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                        Margin="{TemplateBinding Padding}"
                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                    <Grid
                        x:Name="RenderedGrid"
                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                        Margin="{TemplateBinding Padding}"
                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                        <ContentPresenter
                            x:Name="ContentPresenter"
                            ContentTemplate="{TemplateBinding ContentTemplate}"
                            ContentTransitions="{TemplateBinding ContentTransitions}"
                            Content="{TemplateBinding Content}" />
                    </Grid>
                    <Image
                        x:Name="ForegroundFxImage"
                        Stretch="None"
                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                        Margin="{TemplateBinding Padding}"
                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

这是代码:

using System;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using WinRTXamlToolkit.Imaging;

namespace WinRTXamlToolkit.Controls
{
    public class FxContentControl : ContentControl
    {
        private Image _backgroundFxImage;
        private Image _foregroundFxImage;
        private ContentPresenter _contentPresenter;
        private Grid _renderedGrid;

        public FxContentControl()
        {
            this.DefaultStyleKey = typeof(FxContentControl);
        }

        protected override async void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _backgroundFxImage = this.GetTemplateChild("BackgroundFxImage") as Image;
            _foregroundFxImage = this.GetTemplateChild("ForegroundFxImage") as Image;
            _contentPresenter = this.GetTemplateChild("ContentPresenter") as ContentPresenter;
            _renderedGrid = this.GetTemplateChild("RenderedGrid") as Grid;

            if (_renderedGrid != null)
            {
                _renderedGrid.SizeChanged += this.OnContentPresenterSizeChanged;
            }

            if (_renderedGrid.ActualHeight > 0)
            {
                await this.UpdateFx();
            }
        }

        private async void OnContentPresenterSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
        {
            await this.UpdateFx();
        }

        private async Task UpdateFx()
        {
            await this.UpdateBackgroundFx();
        }

        private async Task UpdateBackgroundFx()
        {
            if (_renderedGrid.ActualHeight < 1 ||
                _backgroundFxImage == null)
            {
                return;
            }

            var rtb = new RenderTargetBitmap();
            await rtb.RenderAsync(_renderedGrid);

            var pw = rtb.PixelWidth;
            var ph = rtb.PixelHeight;

            var wb = _backgroundFxImage.Source as WriteableBitmap;

            if (wb == null ||
                wb.PixelWidth != pw ||
                wb.PixelHeight != ph)
            {
                wb = new WriteableBitmap(pw, ph);
            }

            await ProcessContentImage(rtb, wb, pw, ph);

            _backgroundFxImage.Source = wb;
        }

        protected virtual async Task ProcessContentImage(RenderTargetBitmap rtb, WriteableBitmap wb, int pw, int ph)
        {
            var rtbBuffer = await rtb.GetPixelsAsync();
            var rtbPixels = rtbBuffer.GetPixels();
            var wbBuffer = wb.PixelBuffer;
            var wbPixels = wbBuffer.GetPixels();

            // Expand
            int expansion = 1;

            for (int x = 0; x < pw; x++)
                for (int y = 0; y < ph; y++)
                {
                    int x1min = Math.Max(0, x - expansion);
                    int x1max = Math.Min(x + expansion, pw - 1);
                    int y1min = Math.Max(0, y - expansion);
                    int y1max = Math.Min(y + expansion, ph - 1);
                    //bool found = false;
                    byte maxa = 0;

                    for (int x1 = x1min; x1 <= x1max; x1++)
                        for (int y1 = y1min; y1 <= y1max; y1++)
                        {
                            var a = rtbPixels.Bytes[4 * (y1 * pw + x1) + 3];
                            if (a > maxa)
                                maxa = a;
                        }
                    wbPixels.Bytes[4 * (y * pw + x)] = 0;
                    wbPixels.Bytes[4 * (y * pw + x) + 1] = 0;
                    wbPixels.Bytes[4 * (y * pw + x) + 2] = 0;
                    wbPixels.Bytes[4 * (y * pw + x) + 3] = maxa;
                }

            wbPixels.UpdateFromBytes();
        }
    }
}

它还使用以下 IBuffer 包装器:

using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage.Streams;

namespace WinRTXamlToolkit.Imaging
{
// ReSharper disable InconsistentNaming - This class extends IBuffer
    /// <summary>
    /// Contains extensions for an IBuffer interface in the context of a WriteableBitmap,
    /// which exposes one to access its pixels.
    /// </summary>
    public static class IBufferExtensions
// ReSharper restore InconsistentNaming
    {
        /// <summary>
        /// Gives access to the pixels of a WriteableBitmap given an IBuffer
        /// exposed by Pixels property.
        /// </summary>
        /// <remarks>
        /// Note that creating this object copies the pixels buffer
        /// into the Bytes byte array for quick pixel access
        /// and the array needs to be copied back to the pixels buffer
        /// to update the bitmap with a call to UpdateFromBytes().
        /// This is acceptable for convenience and possibly best for
        /// performance in some scenarios, but it does add some upfront
        /// overhead as well overhead to update the bitmap at the end.
        /// This is only a theory and for better performance it might be
        /// good to test different approaches.
        /// The goal of this approach is code simplicity. For best performance
        /// using native code and/or DirectX is recommended.
        /// </remarks>
        public class PixelBufferInfo
        {
            private readonly Stream _pixelStream;

            /// <summary>
            /// The bytes of the pixel stream.
            /// </summary>
            public byte[] Bytes;

            /// <summary>
            /// Initializes a new instance of the <see cref="PixelBufferInfo" /> class.
            /// </summary>
            /// <param name="pixelBuffer">The pixel buffer returned by WriteableBitmap.PixelBuffer.</param>
            public PixelBufferInfo(IBuffer pixelBuffer)
            {
                _pixelStream = pixelBuffer.AsStream();
                this.Bytes = new byte[_pixelStream.Length];
                _pixelStream.Seek(0, SeekOrigin.Begin);
                _pixelStream.Read(this.Bytes, 0, Bytes.Length);
                //this.Pixels = bytes.ToPixels();
            }

            /// <summary>
            /// Updates the associated pixel buffer from bytes.
            /// </summary>
            public void UpdateFromBytes()
            {
                _pixelStream.Seek(0, SeekOrigin.Begin);
                _pixelStream.Write(Bytes, 0, Bytes.Length);
            }
        }

        /// <summary>
        /// Gets the pixels access wrapper for a PixelBuffer property of a WriteableBitmap.
        /// </summary>
        /// <param name="pixelBuffer">The pixel buffer.</param>
        /// <returns></returns>
        public static PixelBufferInfo GetPixels(this IBuffer pixelBuffer)
        {
            return new PixelBufferInfo(pixelBuffer);
        }
    }
}
于 2013-10-29T04:29:27.410 回答