WPF中实现九宫格绘图

1、问题的起源。

     首先我们看一下QQ2009的面板,普通状态下是这样的:

    

当我们最大化或是拉伸界面后,界面会变成这个样子:

我们可以看到,经过拉伸后的界面背景并没有被等比例拉伸,通过分析我们发现,其实界面是用了一张背景图片,原始图如下:

    通过对比我们可以看到,界面在帖图时采取了四角固定,中间部分拉伸的方法来保证背景在拉伸时不变形。按照一般的绘制方法,我们首先会将

背景图片使用工具来切成9张图片,示意图如下:

如果上图中红线所示,我们通过一个“井”字把整张图片分成了9块,从上到下,从左到右我们将他编号为1-9号。其中第一、三、七、九块为固定大小。

二、八、四、六为水平或是上下拉伸,第五块为双向拉伸。

按照我们传统的帧图方法,我们会采用这样的方式来操作:

第一步:帖一号块,即左上块。

第二步:根据当前窗口宽度帖第二块和第八块,平铺或是拉伸都可以。

第三步:帖第三号块,即上右部分。

第四步:帖第四、六块,即二边。

第五步:帖七号块,即左下部分。

第六步:帖九号块,即右下部分。

第七步:中间部分拉伸。

通过以上七步,我们基本上达到了上面的效果。

但是如果美工修改了背景图片,导致切片大小变化,那我们只能重新修改上述七步中的代码来重新计算每一块的位置。重新帖图。

2、九宫帖图法。

通过上述七步我们可以发现,对于采用整张背景图片做界面背景时,我们的“井”字其实只是确定了图片拉伸区域与固定区域的位置而已。通过进一步观察我们

可以发现,其实只需要我们确定完中间的四边型(第五块)后,整个图片的分割就基本已经确定。于是有人使用了下面的帖图方法:

第一步:设置背景图片的裁剪中心区域,以一个Rect或是Padding的方式来描述。分别对应固定部分对应于背景图片的距离,如本例中为65,,12,65,65。

第二步:根据设置的有参数对整张图片进行局部帖图,帧图步骤类似传统方法。

3、解决办法。

系统自带的图片对象是不具备九宫格绘图功能的,我们可以通过继承系统的Image对象,增加九宫格绘图功能。

首先,我们使用BitmapSource.CopyPixels方法,将要用于绘制的图片进行剪裁,代码如下:

View Code
 1 public static ImageSource SplitImage(BitmapSource source, Int32Rect clipRect)
 2         {
 3             ImageSource results = null;
 4             var stride = clipRect.Width * ((source.Format.BitsPerPixel + 7) / 8);
 5             var pixelsCount = clipRect.Width * clipRect.Height;//tileWidth * tileHeight;
 6             var tileRect =  new Int32Rect(0, 0,clipRect.Width, clipRect.Height);
 7 
 8               var pixels = new int[pixelsCount];
 9             //var copyRect = new Int32Rect(col * tileWidth, row * tileHeight, tileWidth, tileHeight);
10             source.CopyPixels(clipRect, pixels, stride, 0);
11             var wb = new WriteableBitmap(
12                 clipRect.Width,
13                 clipRect.Height,
14                 source.DpiX,
15                 source.DpiY,
16                 source.Format,
17                 source.Palette);
18             wb.Lock();
19             wb.WritePixels(tileRect, pixels, stride, 0);
20             wb.Unlock();
21 
22             results = wb;
23                      return results;
24         }

第二步,根据定义的“井”字中间部分RECT,将源图片分成九块,代码如下:

View Code
public static ImageSource[] Get9CellImageSource(BitmapSource source, Int32Rect clipRect)
        {
            ImageSource[] results = new ImageSource[9];
            Int32Rect rect = Int32Rect.Empty;
            int rightSideWidth = (int)(source.PixelWidth - clipRect.X - clipRect.Width);
            //top-left
            rect.Width = clipRect.X;
            rect.Height = clipRect.Y;
            results[0] = SplitImage(source, rect);
            //return results;
            //top-middle
            rect.X += rect.Width;
            rect.Width = clipRect.Width;
            results[1] = SplitImage(source, rect);
            //top-right
            rect.X += rect.Width;
            rect.Width = rightSideWidth;
            results[2] = SplitImage(source, rect);

            //left side
            rect = Int32Rect.Empty;
            rect.Y = clipRect.Y;
            rect.Width = clipRect.X;
            rect.Height = clipRect.Height;
            results[3] = SplitImage(source, rect);

            //middle
            rect.X += rect.Width;
            rect.Width = clipRect.Width;
            results[4] = SplitImage(source, rect);

            //right side
            rect.X += rect.Width;
            rect.Width = rightSideWidth;
            results[5] = SplitImage(source, rect);
            //bottom-left
            rect = Int32Rect.Empty;
            rect.Y = clipRect.Y + clipRect.Height;
           // rect.X = clipRect.X;
            rect.Height = source.PixelHeight - clipRect.Height - clipRect.Y;
            rect.Width = clipRect.X;
            results[6] = SplitImage(source, rect);

            //bottom-middle
            rect.X += rect.Width;
            rect.Width = clipRect.Width;
            results[7] = SplitImage(source, rect);
            //bottom-right
            rect.X += rect.Width;
            rect.Width = rightSideWidth;
            results[8] = SplitImage(source, rect);
            return results;
        }

第三步:重写Image对象的OnRender方法,使之具备九宫绘图功能,代码如下:

View Code
  1  protected override void OnRender(System.Windows.Media.DrawingContext dc)
  2         {
  3 
  4             if (ClipRect != Int32Rect.Empty)
  5             {
  6                 //剪裁绘图
  7              DrawBitblt(dc);
  8                 return;
  9             }
 10             if (DrawImageWith9Cells == false)
 11             {
 12 
 13                 base.OnRender(dc);
 14                 return;
 15             }
 16             RenderWith9Cells(dc);
 17         }
 18 
 19         private void DrawBitblt(DrawingContext dc)
 20         {
 21 
 22             //增加剪裁后九宫
 23             if (DrawImageWith9Cells == false)
 24             {
 25                 ImageSource source = GetImageSource();
 26                 Rect rect = new Rect(new Point(0, 0), new Size(ActualWidth, ActualHeight));
 27                 dc.DrawImage(source, rect);
 28             }
 29             else
 30             {
 31                 RenderWith9Cells(dc);
 32             }
 33         }
 34 
 35         /// <summary>
 36         /// 9格绘图
 37         /// </summary>
 38         /// <param name="dc"></param>
 39         private void RenderWith9Cells(System.Windows.Media.DrawingContext dc)
 40         {
 41             if (Source == null) return;
 42             if (ClipPadding.Right == 0 || ClipPadding.Bottom == 0) return;
 43 
 44             ImageSource source = Source;
 45             Uri u =new Uri(source.ToString());
 46             BitmapSource image = new BitmapImage(u);
 47             if (ClipRect != Int32Rect.Empty)
 48             {
 49                 //预剪裁
 50                 image = ImageClip.SplitImage(image, ClipRect) as BitmapSource;
 51             }
 52             double contentWidth = image.PixelWidth - ClipPadding.Left - ClipPadding.Right;
 53             double contentHeight = image.PixelHeight - ClipPadding.Top - ClipPadding.Bottom;
 54             Int32Rect contentRect = new Int32Rect((int)ClipPadding.Left, (int)ClipPadding.Top
 55                                                                             , (int)contentWidth, (int)contentHeight);
 56 
 57             //  Rect r = new Rect(new Point(),RenderSize);
 58             //  dc.DrawImage(image,r);
 59             //return;
 60             // image.BeginInit();
 61             // image.EndInit(); 
 62             ImageSource[] images = ImageClip.Get9CellImageSource(image, contentRect);
 63             if (images == null || images.Length != 9)
 64             {
 65                 base.OnRender(dc);
 66                 return;
 67             }
 68             DrawFrame(dc, images, contentRect);
 69             // DrawContent(contentRect, dc);
 70         }
 71 
 72 
 73 
 74         /// <summary>
 75         /// 绘制边框
 76         /// </summary>
 77         /// <param name="dc"></param>
 78         private void DrawFrame(System.Windows.Media.DrawingContext drawingContext, ImageSource[] images, Int32Rect contentRect)
 79         {
 80             Rect drawRect = new Rect(new Point(), new Size(contentRect.X, contentRect.Y));
 81             double drawWidth = ActualWidth - ClipPadding.Left - ClipPadding.Right;
 82             double drawHeight = ActualHeight - ClipPadding.Top - ClipPadding.Bottom;
 83             drawingContext.DrawImage(images[0], drawRect);
 84 
 85             drawRect.X += drawRect.Width;
 86             drawRect.Width = drawWidth;
 87             drawingContext.DrawImage(images[1], drawRect);
 88             //for (int i = (int)drawRect.X; i < ActualWidth - contentRect.Width - contentRect.X; i += contentRect.Width)
 89             //{
 90             //    drawRect.X += drawRect.Width;
 91             //    drawRect.Width = contentRect.Width;
 92             //    drawingContext.DrawImage(images[1], drawRect);
 93             //}
 94             drawRect.X += drawRect.Width;
 95             drawRect.Width = ClipPadding.Right;
 96             drawingContext.DrawImage(images[2], drawRect);
 97             //中间
 98 
 99             drawRect.X = 0;
100             drawRect.Y = contentRect.Y;
101             drawRect.Width = contentRect.X;
102             drawRect.Height = drawHeight;
103             drawingContext.DrawImage(images[3], drawRect);
104 
105             drawRect.X += drawRect.Width;
106             drawRect.Width = drawWidth;
107             drawingContext.DrawImage(images[4], drawRect);
108 
109             drawRect.X += drawRect.Width;
110             drawRect.Width = ClipPadding.Right;
111             drawingContext.DrawImage(images[5], drawRect);
112 
113             //下边
114             drawRect.X = 0;
115             drawRect.Y = ActualHeight - ClipPadding.Bottom;
116             drawRect.Width = ClipPadding.Left;
117             drawRect.Height = ClipPadding.Bottom;
118             drawingContext.DrawImage(images[6], drawRect);
119 
120             drawRect.X += drawRect.Width;
121             drawRect.Width = drawWidth;
122             drawingContext.DrawImage(images[7], drawRect);
123 
124             drawRect.X += drawRect.Width;
125             drawRect.Width = ClipPadding.Right;
126             drawingContext.DrawImage(images[8], drawRect);
127 
128 
129         }
130 
131         private void DrawClip(Rect clipRect, System.Windows.Media.DrawingContext drawingContext, Rect targetRect)
132         {
133             Rect rect = targetRect;// new Rect(new Point(0, 0), targetRect);
134             RectangleGeometry rectangleGeometry = new RectangleGeometry(clipRect);
135             rectangleGeometry.Freeze();
136             drawingContext.PushClip(rectangleGeometry);
137             drawingContext.DrawImage(Source, rect);
138             drawingContext.Pop();
139         }

这样,我们的Image扩展控制就具备了如下功能:
1、原系统中Image控制的功能,即绘制整张图片。

2、绘制大图中指定区域部分。

3、九宫格绘图。

 在XAML中使用:

首先增加本地解决方案命名空间,此处名为l。

<l:ImageEx Name="equipmentback2" DrawImageWith9Cells="True" Source="Images\QQBack.png" ClipPadding="8,58,8,16" Stretch="Fill"></l:ImageEx>

注:

搞了半天不知道怎么发附件,需要完整源代码的朋友可以EMAIL给我。

niaoked@qq.com

 

 

转载于:https://www.cnblogs.com/niu2012/archive/2012/12/15/2818999.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值