none
背景图ImageBrush怎么平铺呢? RRS feed

  • 问题

  •     
      <ImageBrush x:Key="CommonBgBrush"  ImageSource="../Images/MainBg.png"   />  怎么达到平铺的效果,做metro 好多列表是横向的,导致界面宽度不固定,应用的背景图是可以平铺的,但是怎么实现呢,wpf中我看是很好的支持,但8还未找到。。。
    2012年6月20日 10:08

答案

  • Metro XAML中没有提供 VisualBrush 类型,所以无法达到你要的平铺效果。

    不过通过自己实现用户控件,读入我们指定的图片,通过 WriteableBitmap 来重复绘制也可以达到效果,英文论坛中有人这么实现了:http://social.msdn.microsoft.com/Forums/en-US/winappsuidesign/thread/7a7cf286-04f1-42e2-8d2d-72fc9dcb059a

    我将代码修改了一下,符合RP的版本:

    ......using System.Runtime.InteropServices.WindowsRuntime;
    using Windows.ApplicationModel;
    // The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
    namespace App3
    {
        public sealed partial class TilingBackground : UserControl
        {
            public static readonly DependencyProperty ImageSourceProperty =
            DependencyProperty.Register("ImageSource", typeof(string), typeof(TilingBackground),
            new PropertyMetadata(null, new PropertyChangedCallback(OnImageSourceUriChanged)));
            public string ImageSource
            {
                get { return (string)GetValue(ImageSourceProperty); }
                set { SetValue(ImageSourceProperty, value); }
            }
            async private static void OnImageSourceUriChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                await ((TilingBackground)d).UpdateImageSource();
                ((TilingBackground)d).UpdateBackground();
            }
            private int _sourcePixelWidth;
            private int _sourcePixelHeight;
            private byte[] _sourceFramePixels;
            public TilingBackground()
            {
                InitializeComponent();
                this.Loaded += (o, e) =>
                {   // listen for size changes after control is loaded
                    this.SizeChanged += new SizeChangedEventHandler(TilingBackground_SizeChanged);
                };
            }
            private void TilingBackground_SizeChanged(object sender, SizeChangedEventArgs e)
            {
                if (e.NewSize.Width <= 0 || e.NewSize.Height <= 0)
                {
                    BackgroundImage.Source = null;
                }
                else
                {
                    UpdateBackground();
                }
            }
            async private Task UpdateImageSource()
            {
                StorageFile file = await Package.Current.InstalledLocation.GetFileAsync(ImageSource);
                IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
                BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
                BitmapFrame frame = await decoder.GetFrameAsync(0);
                PixelDataProvider framePixelProvider = await frame.GetPixelDataAsync();
                _sourcePixelWidth = (int)decoder.PixelWidth;
                _sourcePixelHeight = (int)decoder.PixelHeight;
                _sourceFramePixels = framePixelProvider.DetachPixelData();
            }
            async private void UpdateBackground()
            {
                if (_sourceFramePixels == null)
                    await UpdateImageSource();
                WriteableBitmap target = new WriteableBitmap(Convert.ToInt32(Math.Ceiling(this.ActualWidth)), Convert.ToInt32(Math.Ceiling(this.ActualHeight)));
                Stream targetStream = target.PixelBuffer.AsStream();
                int currentSourceY = 0;
                for (int targetY = 0; targetY < target.PixelHeight; targetY++)
                {
                    int currentSourceX = 0;
                    for (int targetX = 0; targetX < target.PixelWidth; targetX++)
                    {
                        int byteRangeStart =
                            currentSourceY * _sourcePixelWidth * 4   // start byte of current row
                            + currentSourceX * 4;                   // byte offset into current row
                        targetStream.WriteByte(_sourceFramePixels[byteRangeStart + 2]); // blue
                        targetStream.WriteByte(_sourceFramePixels[byteRangeStart + 1]); // green
                        targetStream.WriteByte(_sourceFramePixels[byteRangeStart + 0]); // red
                        targetStream.WriteByte(_sourceFramePixels[byteRangeStart + 3]); // alpha
                        currentSourceX = (currentSourceX + 1) % (_sourcePixelWidth);
                    }
                    currentSourceY = (currentSourceY + 1) % _sourcePixelHeight;
                }
                BackgroundImage.Source = target;
            }
        }
    }


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年6月21日 5:55
    版主

全部回复

  •     
      <ImageBrush x:Key="CommonBgBrush"  ImageSource="../Images/MainBg.png"   />  怎么达到平铺的效果,做metro 好多列表是横向的,导致界面宽度不固定,应用的背景图是可以平铺的,但是怎么实现呢,wpf中我看是很好的支持,但8还未找到。。。

    Stretch="UniformToFill"
    2012年6月20日 10:16
  •     
      <ImageBrush x:Key="CommonBgBrush"  ImageSource="../Images/MainBg.png"   />  怎么达到平铺的效果,做metro 好多列表是横向的,导致界面宽度不固定,应用的背景图是可以平铺的,但是怎么实现呢,wpf中我看是很好的支持,但8还未找到。。。


    Stretch="UniformToFill"

    不是填满,那样会导致背景被拉升,背景有纹理,需要的效果是背景repeat, 

    wpf中是这样做的, 现在不清楚win8怎么实现

    1. <Window.Background>  
    2.     <VisualBrush TileMode="Tile" Viewport="0,0,0.5,0.5">  
    3.       <VisualBrush.Visual>  
    4.         <Image Source="image.png"></Image>  
    5.       </VisualBrush.Visual>  
    6.     </VisualBrush>  
    7.   </Window.Background>  
    2012年6月20日 15:08
  • Metro XAML中没有提供 VisualBrush 类型,所以无法达到你要的平铺效果。

    不过通过自己实现用户控件,读入我们指定的图片,通过 WriteableBitmap 来重复绘制也可以达到效果,英文论坛中有人这么实现了:http://social.msdn.microsoft.com/Forums/en-US/winappsuidesign/thread/7a7cf286-04f1-42e2-8d2d-72fc9dcb059a

    我将代码修改了一下,符合RP的版本:

    ......using System.Runtime.InteropServices.WindowsRuntime;
    using Windows.ApplicationModel;
    // The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
    namespace App3
    {
        public sealed partial class TilingBackground : UserControl
        {
            public static readonly DependencyProperty ImageSourceProperty =
            DependencyProperty.Register("ImageSource", typeof(string), typeof(TilingBackground),
            new PropertyMetadata(null, new PropertyChangedCallback(OnImageSourceUriChanged)));
            public string ImageSource
            {
                get { return (string)GetValue(ImageSourceProperty); }
                set { SetValue(ImageSourceProperty, value); }
            }
            async private static void OnImageSourceUriChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                await ((TilingBackground)d).UpdateImageSource();
                ((TilingBackground)d).UpdateBackground();
            }
            private int _sourcePixelWidth;
            private int _sourcePixelHeight;
            private byte[] _sourceFramePixels;
            public TilingBackground()
            {
                InitializeComponent();
                this.Loaded += (o, e) =>
                {   // listen for size changes after control is loaded
                    this.SizeChanged += new SizeChangedEventHandler(TilingBackground_SizeChanged);
                };
            }
            private void TilingBackground_SizeChanged(object sender, SizeChangedEventArgs e)
            {
                if (e.NewSize.Width <= 0 || e.NewSize.Height <= 0)
                {
                    BackgroundImage.Source = null;
                }
                else
                {
                    UpdateBackground();
                }
            }
            async private Task UpdateImageSource()
            {
                StorageFile file = await Package.Current.InstalledLocation.GetFileAsync(ImageSource);
                IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
                BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
                BitmapFrame frame = await decoder.GetFrameAsync(0);
                PixelDataProvider framePixelProvider = await frame.GetPixelDataAsync();
                _sourcePixelWidth = (int)decoder.PixelWidth;
                _sourcePixelHeight = (int)decoder.PixelHeight;
                _sourceFramePixels = framePixelProvider.DetachPixelData();
            }
            async private void UpdateBackground()
            {
                if (_sourceFramePixels == null)
                    await UpdateImageSource();
                WriteableBitmap target = new WriteableBitmap(Convert.ToInt32(Math.Ceiling(this.ActualWidth)), Convert.ToInt32(Math.Ceiling(this.ActualHeight)));
                Stream targetStream = target.PixelBuffer.AsStream();
                int currentSourceY = 0;
                for (int targetY = 0; targetY < target.PixelHeight; targetY++)
                {
                    int currentSourceX = 0;
                    for (int targetX = 0; targetX < target.PixelWidth; targetX++)
                    {
                        int byteRangeStart =
                            currentSourceY * _sourcePixelWidth * 4   // start byte of current row
                            + currentSourceX * 4;                   // byte offset into current row
                        targetStream.WriteByte(_sourceFramePixels[byteRangeStart + 2]); // blue
                        targetStream.WriteByte(_sourceFramePixels[byteRangeStart + 1]); // green
                        targetStream.WriteByte(_sourceFramePixels[byteRangeStart + 0]); // red
                        targetStream.WriteByte(_sourceFramePixels[byteRangeStart + 3]); // alpha
                        currentSourceX = (currentSourceX + 1) % (_sourcePixelWidth);
                    }
                    currentSourceY = (currentSourceY + 1) % _sourcePixelHeight;
                }
                BackgroundImage.Source = target;
            }
        }
    }


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us

    2012年6月21日 5:55
    版主
  • 谢谢版主,我试试。
    2012年6月21日 9:53
  • 呃,有相应C++的写法么?刚涉及C++,才感觉C#好简洁
    2012年9月14日 8:13