MetroGridHelper:WP7设计师与开发人员的得力助手

译文
移动开发
如果最终外观设计方案能够与Windows Phone上现有的性能计数器相似,那么肯定会大受欢迎——在调试时,我们能够将这套网格重叠覆盖在整个应用程序框架上,使其成为独立而完整的全局显示方案。

作者简介:Jeff Wilcox,微软高级软件开发工程师,不久前,调往微软Windows Azure组,负责微软云技术的开源项目。Jeff Wilcox曾负责开发了Windows Phone平台的Foursqaure客户端,并参与了无数Silverlight工具包、Windows Phone 7.0、7.1开发包,Windows Phone用户界面控制、帮助,以及许多其他重要项目。

[[79339]] 
Jeff Wilcox

【51CTO译文】作为沉浸于开发行业多年的老手,大家一定对这些话题并不陌生:整理页面边距、对齐图像内容以及让自己的应用程序更美观。随着软件消费者中“外貌协会”成员的比重不断上升,用户界面不够抢眼的应用几乎已经无法在市场上占得一席之地。

不久前,我有幸参加了Windows Phone设计团队举办的“美观开发空间”活动。我要由衷地赞叹,这是一次真正的创意空间交流活动,到处是有趣的参与者、舒缓的音乐以及为市场创造出更好、更漂亮的应用程序的热烈渴求。

在活动中我一直坐在两位用户体验设计师Corrina与Arturo身边,与他们讨论设计工作中的原则性取向;很偶然地,我们的话题转移到一副简洁而相当美观的网格图上,这也正是二位在Windows Phone平台上开发的主要焦点。图像由多个红色正方形构成,每个方形为25x25像素,两个方形之间相隔12像素,也就是说每个独立单元都拥有宽高为24像素的反衬背景。(还是那句话,Metro风格要求边框长度始终为12的整数)

设计将使用典型的Photoshop层来容纳这些方形,或者是在应用程序页面之上插入XAML借以完成图形对齐、网格设计及位置调整等等。

我的想法是:如果最终外观设计方案能够与Windows Phone上现有的性能计数器相似,那么肯定会大受欢迎——在调试时,我们能够将这套网格重叠覆盖在整个应用程序框架上,使其成为独立而完整的全局显示方案。就在活动过程当中,我即兴用代码将自己的想法表达了出来,下面请大家分享我的成果。

要使用这款计数器,我们只需打开App.xaml.cs文件(这里囊括了其它多款性能计数器),并将其添加进来。如果大家只是打算简单设计一下,那么我建议各位直接将其启用,这样一来我们就能够让它作用于实机及模拟器中的应用程序。之所以要把它与模拟器关联起来,是因为我们能够将最终显示效果通过截图与朋友及家人分享,并聆听他们在图像位置方面提出的意见。

  1. // Show graphics profiling information while debugging.  
  2. if (System.Diagnostics.Debugger.IsAttached)  
  3. {  
  4.     // Display the current frame rate counters.  
  5.     Application.Current.Host.Settings.EnableFrameRateCounter = true;  
  6.    
  7.     // Display the metro grid helper.  
  8.     MetroGridHelper.IsVisible = true

以下是这段简单的代码在与小型应用程序协作时显示出的效果:

效果

在这个例子中,我遇到了Windows Phone设计中的经典“bug”:文本信息区块之一在插入过程中未能正确显示应有的Metro风格。也就是说,该区块的左侧边距为“0”,而不是Metro要求的12像素,这使得对应文字内容比其它字体更靠左。通过上图中正方形的对比,相信大家能更清晰地理解我遇到的问题,字体错位现象十分明显。

如果大家不喜欢默认的红色以及~0.15的不透明度,我还为不透明度及颜色添加了简单的静态属性设置选项,希望能让各位获得自己理想中的方形单元效果。在运行过程中,可见属性不会显示出来,但请大家注意,只要网格本身仍然存在于可视化元素列表当中,就会占用对应的性能资源(因此请务必在应用程序的发布版本中把网格去掉,否则会造成毫无意义的资源浪费)。

源代码

我已经在NuGet上发布过源文件——这应该是大家在自己的项目中使用这款小成品的最佳方式。如果今后我做出任何修正或添加某些功能,各位也将会在NuGet网站上及时找到最新版本。

◆确保自己已经安装了NuGet(http://www.nuget.org/)

◆使用控制台或软件包管理器安装该软件包,我把它命名为MetroGridHelper

PM> Install-Package MetroGridHelper

当然,大家也可以将这部分源代码在项目中整理成一个全新的文件,MetroGridHelper.cs:

  1. // (c) Copyright Microsoft Corporation.  
  2. // This source is subject to the Microsoft Public License (Ms-PL).  
  3. // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.  
  4. // All other rights reserved.  
  5.  
  6. using System.Collections.Generic;  
  7. using System.Diagnostics;  
  8. using System.Windows.Controls;  
  9. using System.Windows.Media;  
  10. using System.Windows.Shapes;  
  11.  
  12. namespace System.Windows  
  13. {  
  14.     /// <summary>  
  15.     /// A utility class that overlays a designer-friendly grid on top of the  
  16.     /// application frame, for use similar to the performance counters in  
  17.     /// App.xaml.cs. The color and opacity are configurable. The grid contains  
  18.     /// a number of squares that are 24x24, offset with 12px gutters, and all  
  19.     /// 24px away from the edge of the device.  
  20.     /// </summary>  
  21.     public static class MetroGridHelper  
  22.     {  
  23.         private static bool _visible;  
  24.         private static double _opacity = 0.15;  
  25.         private static Color _color = Colors.Red;  
  26.         private static List<Rectangle> _squares;  
  27.         private static Grid _grid;  
  28.    
  29.         /// <summary>  
  30.         /// Gets or sets a value indicating whether the designer grid is  
  31.         /// visible on top of the application's frame.  
  32.         /// </summary>  
  33.         public static bool IsVisible  
  34.         {  
  35.             get 
  36.             {  
  37.                 return _visible;  
  38.             }  
  39.             set 
  40.             {  
  41.                 _visible = value;  
  42.                 UpdateGrid();  
  43.             }  
  44.         }  
  45.    
  46.         /// <summary>  
  47.         /// Gets or sets the color to use for the grid's squares.  
  48.         /// </summary>  
  49.         public static Color Color  
  50.         {  
  51.             get { return _color; }  
  52.             set 
  53.             {  
  54.                 _color = value;  
  55.                 UpdateGrid();  
  56.             }  
  57.         }  
  58.    
  59.         /// <summary>  
  60.         /// Gets or sets a value indicating the opacity for the grid's squares.  
  61.         /// </summary>  
  62.         public static double Opacity  
  63.         {  
  64.             get { return _opacity; }  
  65.             set 
  66.             {  
  67.                 _opacity = value;  
  68.                 UpdateGrid();  
  69.             }  
  70.         }  
  71.    
  72.         /// <summary>  
  73.         /// Updates the grid (if it already has been created) or initializes it  
  74.         /// otherwise.  
  75.         /// </summary>  
  76.         private static void UpdateGrid()  
  77.         {  
  78.             if (_squares != null)  
  79.             {  
  80.                 var brush = new SolidColorBrush(_color);  
  81.                 foreach (var square in _squares)  
  82.                 {  
  83.                     square.Fill = brush;  
  84.                 }  
  85.                 if (_grid != null)  
  86.                 {  
  87.                     _grid.Visibility = _visible ? Visibility.Visible : Visibility.Collapsed;  
  88.                     _grid.Opacity = _opacity;  
  89.                 }  
  90.             }  
  91.             else 
  92.             {  
  93.                 BuildGrid();  
  94.             }  
  95.         }  
  96.    
  97.         /// <summary>  
  98.         /// Builds the grid.  
  99.         /// </summary>  
  100.         private static void BuildGrid()  
  101.         {  
  102.             _squares = new List<Rectangle>();  
  103.    
  104.             var frame = Application.Current.RootVisual as Frame;  
  105.             if (frame == null || VisualTreeHelper.GetChildrenCount(frame) == 0)  
  106.             {  
  107.                 Deployment.Current.Dispatcher.BeginInvoke(BuildGrid);  
  108.                 return;  
  109.             }  
  110.    
  111.             var child = VisualTreeHelper.GetChild(frame, 0);  
  112.             var childAsBorder = child as Border;  
  113.             var childAsGrid = child as Grid;  
  114.             if (childAsBorder != null)  
  115.             {  
  116.                 // Not a pretty way to control the root visual, but I did not  
  117.                 // want to implement using a popup.  
  118.                 var content = childAsBorder.Child;  
  119.                 if (content == null)  
  120.                 {  
  121.                     Deployment.Current.Dispatcher.BeginInvoke(BuildGrid);  
  122.                     return;  
  123.                 }  
  124.                 childAsBorder.Child = null;  
  125.                 Deployment.Current.Dispatcher.BeginInvoke(() =>  
  126.                 {  
  127.                     Grid newGrid = new Grid();  
  128.                     childAsBorder.Child = newGrid;  
  129.                     newGrid.Children.Add(content);  
  130.                     PrepareGrid(frame, newGrid);  
  131.                 });  
  132.             }  
  133.             else if (childAsGrid != null)  
  134.             {  
  135.                 PrepareGrid(frame, childAsGrid);  
  136.             }  
  137.             else 
  138.             {  
  139.                 Debug.WriteLine("Dear developer:");  
  140.                 Debug.WriteLine("Unfortunately the design overlay feature requires that the root frame visual");  
  141.                 Debug.WriteLine("be a Border or a Grid. So the overlay grid just isn't going to happen.");  
  142.                 return;  
  143.             }  
  144.         }  
  145.    
  146.         /// <summary>  
  147.         /// Does the actual work of preparing the grid once the parent frame is  
  148.         /// in the visual tree and we have a Grid instance to work with for  
  149.         /// placing the chilren.  
  150.         /// </summary>  
  151.         /// <param name="frame">The phone application frame.</param>  
  152.         /// <param name="parent">The parent grid to insert the sub-grid into.</param>  
  153.         private static void PrepareGrid(Frame frame, Grid parent)  
  154.         {  
  155.             var brush = new SolidColorBrush(_color);  
  156.    
  157.             _grid = new Grid();  
  158.             _grid.IsHitTestVisible = false;  
  159.    
  160.             // To support both orientations, unfortunately more visuals need to  
  161.             // be used. An alternate implementation would be to react to the  
  162.             // orientation change event and re-draw/remove squares.  
  163.             double width = frame.ActualWidth;  
  164.             double height = frame.ActualHeight;  
  165.             double max = Math.Max(width, height);  
  166.    
  167.             for (int x = 24; x < /*width*/ max; x += 37)  
  168.             {  
  169.                 for (int y = 24; y < /*height*/ max; y += 37)  
  170.                 {  
  171.                     var rect = new Rectangle  
  172.                     {  
  173.                         Width = 25,  
  174.                         Height = 25,  
  175.                         VerticalAlignment = System.Windows.VerticalAlignment.Top,  
  176.                         HorizontalAlignment = System.Windows.HorizontalAlignment.Left,  
  177.                         Margin = new Thickness(x, y, 0, 0),  
  178.                         IsHitTestVisible = false,  
  179.                         Fill = brush,  
  180.                     };  
  181.                     _grid.Children.Add(rect);  
  182.                     _squares.Add(rect);  
  183.                 }  
  184.             }  
  185.    
  186.             _grid.Visibility = _visible ? Visibility.Visible : Visibility.Collapsed;  
  187.             _grid.Opacity = _opacity;  
  188.    
  189.             // For performance reasons a single surface should ideally be used  
  190.             // for the grid.  
  191.             _grid.CacheMode = new BitmapCache();  
  192.    
  193.             // Places the grid into the visual tree. It is never removed once  
  194.             // being added.  
  195.             parent.Children.Add(_grid);  
  196.         }  
  197.     }  

 

原文链接http://www.jeff.wilcox.name/2011/10/metrogridhelper/    核子可乐 译

责任编辑:王晓东 来源: 51CTO.com
相关推荐

2019-09-01 23:48:59

开发代码编程

2024-02-06 09:53:45

Pythonget()函数Dictionary

2013-03-26 13:45:34

开发人员设计师创业

2010-08-16 09:21:35

Windows Pho

2010-04-16 10:49:38

2011-04-30 16:56:45

Windows PhoiOS

2015-07-28 16:38:56

App移动开发

2023-06-01 08:17:37

UX开发

2016-01-25 09:32:37

容器网络SDN

2012-06-04 15:38:34

台式机评测

2023-11-21 09:11:31

2012-05-30 15:15:42

ibmdw

2023-03-15 07:12:53

企业开发人员提供商

2016-03-09 12:11:33

Web开发人员简单步骤

2021-04-15 09:42:21

程序开发软件开发

2010-08-09 16:09:25

2021-04-18 22:11:55

程序员开发代码

2013-09-25 09:20:39

iOS开发iOS7iPhone5s

2009-12-11 14:50:14

Visual Basi

2009-11-23 20:07:51

ibmdw开发
点赞
收藏

51CTO技术栈公众号