xaml - Winrt 中的网格效果

标签 xaml windows-runtime winrt-xaml

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

最佳答案

不执行此操作或使用 DirectX 的替代方法可能是使用新的 RenderTargetBitmap 类以及 RenderAsync() 方法,该方法允许您访问从 C# 或 VB 级别进行控制(假设您正在使用该级别),这可能比 DirectX 更容易,而且实际上速度足够快,足以让一些基本效果在 CPU 上运行。我已经在 FxContentControl 中完成了上周末在 WinRT XAML 工具包中,它似乎在一些简单的场景中工作正常,主要问题是 - 何时更新效果。

该控件目前不会模糊任何内容,而是找到内容的扩展像素并将其设为黑色,作为为 TextBlock 或任何其他控件生成笔划的穷人的方式有一些透明区域和一些不透明区域。您可以将其更改为模糊或添加一个选项以相当轻松地按需执行此操作。

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

<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);
        }
    }
}

关于xaml - Winrt 中的网格效果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19596177/

相关文章:

c# - XAML 如何实例化对象?

xaml - 如何链接 View 和 ViewModel 以及在哪里创建它们

c# - 在 WinRT 中绑定(bind)到 XAML 中的 DynamicObjects

xaml - 为什么 FlipView 会忽略 SelectedItem

xaml - 如何在关闭时为 SettingsFlyout 设置动画

c# - 如何创建在用户设置的特定日期和时间弹出的WPF C#应用程序?

c# - 如何在另一种样式中使用一种样式的值?

java - Java 虚拟机可以在 WinRT 上运行吗?

windows - 为什么在 ScrollViewer 内部单击时我的 TextBox 会聚焦?

c# - MVVM Light Dispatcher 帮助程序设计时错误