试验平台:.Net Micro Framework 模拟器
在前几篇关于.Net Micro Framework的研究文章中,我对它的绘图功能实不敢恭维,不过微软的MF开发人员很聪明,对位图方面的功能实现的就比较完善,这样做起图形应用来就不至于捉襟见肘了。
前段时间用.Net Compact Framework实现了一个奥运场馆查询(相关文章请参见:http://yfsoft.blog.51cto.com/1635641/322933),现在我们用.Net Micro Framework也实现一个,看看哪一个更炫。

 

.Net Micro Framework研究—应用实例_Framework .Net Micro Framework研究—应用实例_Micro_02


(效果是不是比用.Net Compact Framework做的还棒!)
我这个程序脱胎于MF的示例文件NewPresentation,不过我对它进行了大刀阔斧的改进,原程序在实现菜单时至少需要两套大小不同的图片,现在我已经修改成仅需要一套图片了,此外我对tinyfnt中文字库的创建程序又进行了优化,可以更高效、更完美的创建字库了(相关文章请参见:http://yfsoft.blog.51cto.com/1635641/322943)。
如优化前菜单添加代码:
MenuItem menuItem1 = new MenuItem(Resources.BitmapResources.Vertical_Stack_Panel_Icon_Small, Resources.BitmapResources.Vertical_Stack_Panel_Icon, "Vertical Stack Panel", Resources.BitmapResources.Canvas_Panel_Icon);
      MenuItem menuItem2 = new MenuItem(Resources.BitmapResources.Horizontal_Stack_Panel_Icon_Small, Resources.BitmapResources.Horizontal_Stack_Panel_Icon, "Horizontal Stack Panel", Resources.BitmapResources.Canvas_Panel_Icon);
      MenuItem menuItem3 = new MenuItem(Resources.BitmapResources.Canvas_Panel_Icon_Small, Resources.BitmapResources.Canvas_Panel_Icon, "Canvas Panel", Resources.BitmapResources.Canvas_Panel_Icon);
      MenuItem menuItem4 = new MenuItem(Resources.BitmapResources.Diagonal_Panel_Icon_Small, Resources.BitmapResources.Diagonal_Panel_Icon, "Diagonal Panel", Resources.BitmapResources.Canvas_Panel_Icon);
      MenuItem menuItem5 = new MenuItem(Resources.BitmapResources.Scrollable_Panel_Icon_Small, Resources.BitmapResources.Scrollable_Panel_Icon, "Scrollable Panel", Resources.BitmapResources.Canvas_Panel_Icon);
      MenuItem menuItem6 = new MenuItem(Resources.BitmapResources.Free_Drawing_Panel_Icon_Small, Resources.BitmapResources.Free_Drawing_Panel_Icon, "Free Drawing Panel", Resources.BitmapResources.Canvas_Panel_Icon);
 
      // Add each of the menu items to the menu item panel
      m_MenuItemPanel.AddMenuItem(menuItem1);
      m_MenuItemPanel.AddMenuItem(menuItem2);
      m_MenuItemPanel.AddMenuItem(menuItem3);
      m_MenuItemPanel.AddMenuItem(menuItem4);
      m_MenuItemPanel.AddMenuItem(menuItem5);
 m_MenuItemPanel.AddMenuItem(menuItem6);
 
优化后的代码:
   m_MenuItemPanel.AddMenuItem(new MenuItem(Resources.BitmapResources.M1, "国家体育馆"));
   m_MenuItemPanel.AddMenuItem(new MenuItem(Resources.BitmapResources.M2, "北京大学体育馆"));
   m_MenuItemPanel.AddMenuItem(new MenuItem(Resources.BitmapResources.M3, "国家游泳中心-水立方"));
   m_MenuItemPanel.AddMenuItem(new MenuItem(Resources.BitmapResources.M4, "国家体育场-鸟巢"));
   m_MenuItemPanel.AddMenuItem(new MenuItem(Resources.BitmapResources.M5, "北京射击馆"));
   m_MenuItemPanel.AddMenuItem(new MenuItem(Resources.BitmapResources.M6, "顺义奥林匹克水上公园"));
 
实事求是的说,MF示例实现的图形菜单动画比我在北京2008奥运场馆速查中实现的要好,它的更规范,更具有通用性。相关代码如下:
 
 

  1. // 菜单类  
  2. internal sealed class MenuItemPanel : Control  
  3. {  
  4.     private int _currentChild = 0;  
  5.     private int _width;  
  6.     private int _height;  
  7.     private int _animationStep;  
  8.     private Color ForeColor;  
  9.     public ArrayList MenuItemList;  
  10.    
  11.     public MenuItemPanel(int width, int height, Color ForeColor,Color BackColor)  
  12.     {  
  13.         //背景色  
  14.         Background = new SolidColorBrush(BackColor);  
  15.         //前景色  
  16.         this.ForeColor = ForeColor;  
  17.         //大小  
  18.         _width = width;  
  19.         _height = height;  
  20.         //菜单选项集合  
  21.         MenuItemList = new ArrayList();  
  22.     }  
  23.       
  24.     //添加菜单选项  
  25.     public void AddMenuItem(MenuItem menuItem)  
  26.     {  
  27.         MenuItemList.Add(menuItem);  
  28.     }  
  29.    
  30.    //当前菜单选项索引  
  31.     public int CurrentChild  
  32.     {  
  33.         get { return _currentChild; }  
  34.         set 
  35.         {  
  36.             if (value > _currentChild) _animationStep = maxStep;             // 向右移动  
  37.             else if (value < _currentChild) _animationStep = -maxStep;       // 向左移动  
  38.             else _animationStep = 0;                                         // 没有移动  
  39.    
  40.             if (value >= MenuItemList.Count) value = 0;                      // 菜单项是一个环状结构  
  41.             if (value < 0) value = MenuItemList.Count - 1;                         
  42.    
  43.             //开始重绘  
  44.             if (_animationStep != 0)  
  45.             {  
  46.                 _currentChild = value;  
  47.                 Invalidate();                                                //开始移动  
  48.             }  
  49.         }  
  50.     }  
  51.    
  52.     static public int maxStep = 5;      //动画帧数  
  53.     const int xOffsetSeparation =2;     //每个菜单项的间距  
  54.     const int timerInterval = 20;       //移动每一个帧之间的动画时间ms  
  55.    
  56.     //绘制菜单  
  57.     public override void OnRender(DrawingContext dc)  
  58.     {  
  59.         base.OnRender(dc);  
  60.    
  61.         //获得图片宽度  
  62.         int largeX = ((MenuItem)MenuItemList[0]).ImageWidth + xOffsetSeparation;  
  63.    
  64.         //起始坐标  
  65.         int x = (_width / 2) - ((largeX * 2) + (largeX / 2));  
  66.         int y =22;  
  67.    
  68.         //缩放比例  
  69.         int scale = 0;  
  70.    
  71.         //缩放比例的步长  
  72.         int scaleOffset = System.Math.Abs(_animationStep);  
  73.    
  74.         //X方向移动  
  75.         x += _animationStep * 5;  
  76.    
  77.         //绘背景  
  78.         dc.DrawImage(Resources.GetBitmap(Resources.BitmapResources.Main), 0, 0);  
  79.    
  80.         #region //绘菜单图像  
  81.         for (int i = _currentChild - 2; i < _currentChild + 3; i++)  
  82.         {  
  83.             if (i == _currentChild) //中间图像  
  84.             {  
  85.                 scale = maxStep - scaleOffset;  
  86.             }  
  87.             else //两边图像  
  88.             {  
  89.                 if ((_animationStep < 0 && i == _currentChild + 1) || (_animationStep > 0 && i == _currentChild - 1))  
  90.                     scale = scaleOffset;  
  91.                 else 
  92.                     scale = 0;  
  93.             }  
  94.    
  95.             //当前菜单项  
  96.             MenuItem menuItem = null;  
  97.             if (i < 0) menuItem = (MenuItem)MenuItemList[MenuItemList.Count + i];  
  98.             else if (i > MenuItemList.Count - 1) menuItem = (MenuItem)MenuItemList[i - MenuItemList.Count];  
  99.             else menuItem = (MenuItem)MenuItemList[i];  
  100.    
  101.             menuItem.Render(dc, x, y, scale);  
  102.             x += largeX;  
  103.         }  
  104.         #endregion  
  105.    
  106.         #region //写菜单文本  
  107.         if (_width > 0)  
  108.         {  
  109.             int step = 20;  
  110.             int row = 125;  
  111.             if (_width < _height) step = 40;  
  112.    
  113.             //写说明  
  114.             string text = ((MenuItem)MenuItemList[_currentChild]).Description;  
  115.             dc.DrawText(ref text, Resources.GetFont(Resources.FontResources.china2008), ForeColor, 10, row, _width - 20, step, TextAlignment.Center, TextTrimming.None);  
  116.         }  
  117.         #endregion  
  118.    
  119.         //启动动画时钟  
  120.         StartAnimationTimer();  
  121.     }  
  122.    
  123.     private DispatcherTimer _animationTimer;  
  124.     private void StartAnimationTimer()  
  125.     {  
  126.         if (_animationStep != 0)  
  127.         {  
  128.             if (_animationTimer == null)  
  129.             {  
  130.                 _animationTimer = new DispatcherTimer(this.Dispatcher);  
  131.                 _animationTimer.Interval = new TimeSpan(0, 0, 0, 0, timerInterval);  
  132.                 //每隔一段时间触发该事件  
  133.                 _animationTimer.Tick += new EventHandler(OnAnimationTimer);  
  134.             }  
  135.             _lastTick = DateTime.Now.Ticks;  
  136.             _animationTimer.Start();  
  137.         }  
  138.     }  
  139.     long _lastTick = 0;  
  140.     private void OnAnimationTimer(object o, EventArgs e)  
  141.     {  
  142.         _animationTimer.Stop();  
  143.         long ms = ((DateTime.Now.Ticks - _lastTick) / 10000);  
  144.         _lastTick = DateTime.Now.Ticks;  
  145.         int increment = (int)(ms / timerInterval);  
  146.    
  147.         if (increment < 1) increment = 1;  
  148.         else if (increment > maxStep) increment = maxStep;  
  149.    
  150.         if (_animationStep < 0) _animationStep += increment;  
  151.         else if (_animationStep > 0) _animationStep -= increment;  
  152.    
  153.         //重新触发OnRender函数的执行  
  154.         Invalidate();  
  155.     }  
  156.    
  157.     //窗体可用尺寸  
  158.     protected override void MeasureOverride(int availableWidth, int availableHeight, out int desiredWidth, out int desiredHeight)  
  159.     {  
  160.         desiredWidth = _width;  
  161.         desiredHeight = _height;  
  162.     }  
  163. }  
  164.    
  165. // 菜单选项类  
  166. internal sealed class MenuItem : Control  
  167. {  
  168.     private Bitmap _p_w_picpath;         // 菜单图标  
  169.     private string _description;   // 菜单说明  
  170.     private int[] _widthSteps;     // 宽度步长数组  
  171.     private int[] _heightSteps;    // 高度步长数组  
  172.     public int ImageWidth;         // 图像宽度  
  173.     public int ImageHeight;        // 图像高度  
  174.     private float fScale = (float)0.8;   // 缩小因子  
  175.     public int SmallWidth;         // 缩小后的宽度  
  176.     public int SmallHeight;        // 缩小后的高度  
  177.    
  178.     public MenuItem()  
  179.     {  
  180.     }  
  181.     public MenuItem(Resources.BitmapResources rBitmap, string description)  
  182.     {  
  183.         //菜单图像  
  184.         _p_w_picpath = Resources.GetBitmap(rBitmap);  
  185.           
  186.         //图像尺寸  
  187.         ImageWidth = _p_w_picpath.Width;  
  188.         ImageHeight = _p_w_picpath.Height;  
  189.         SmallWidth = (int)(fScale * ImageWidth);  
  190.         SmallHeight = (int)(fScale * ImageHeight);  
  191.    
  192.         //菜单说明  
  193.         _description = description;  
  194.    
  195.         //创建中间动画帧尺寸数组  
  196.         _widthSteps = new int[MenuItemPanel.maxStep];  
  197.         _heightSteps = new int[MenuItemPanel.maxStep];  
  198.    
  199.         //动画变化总大小  
  200.         int wDiff = ImageWidth - SmallWidth;  
  201.         int hDiff = SmallHeight - SmallHeight;  
  202.    
  203.         //保存每次变化的大小  
  204.         for (int i = 1; i < MenuItemPanel.maxStep; i++)  
  205.         {  
  206.             _widthSteps[i] = (wDiff / MenuItemPanel.maxStep) * i;  
  207.             _heightSteps[i] = (hDiff / MenuItemPanel.maxStep) * i;  
  208.         }  
  209.    
  210.     }  
  211.     //菜单说明  
  212.     public string Description  
  213.     {  
  214.         get { return _description; }  
  215.         set { _description = value; }  
  216.     }  
  217.     //菜单绘制  
  218.     public void Render(DrawingContext dc, int x, int y, int scale)  
  219.     {  
  220.         // 图像必须存在  
  221.         if (_p_w_picpath != null)  
  222.         {  
  223.             if (scale == MenuItemPanel.maxStep)  
  224.             {  
  225.                 Width = ImageWidth;  
  226.                 Height = ImageHeight;  
  227.                 dc.DrawRectangle(new SolidColorBrush(Color.Black), new Pen(Color.Black), x+2, y+2, Width, Height);  
  228.                 dc.DrawImage(_p_w_picpath, x, y);  
  229.             }  
  230.             else 
  231.             {  
  232.                 if (scale == 0)  
  233.                 {  
  234.                     Width = SmallWidth;  
  235.                     Height = SmallHeight;  
  236.                     x += ((ImageWidth - Width) / 2);  
  237.                     y += ((ImageHeight - Height) / 2);  
  238.                     dc.DrawRectangle(new SolidColorBrush(Color.Black), new Pen(Color.Black), x + 2, y + 2, Width, Height);  
  239.                     dc.Bitmap.StretchImage(x, y, _p_w_picpath, Width, Height, 255);  
  240.                  }  
  241.                 else 
  242.                 {  
  243.                     int wDiff = ImageWidth - SmallWidth;  
  244.                     int hDiff = SmallHeight - SmallHeight;  
  245.                     Width = SmallWidth + _widthSteps[scale];  
  246.                     Height = SmallHeight + _heightSteps[scale];  
  247.                     x += ((ImageWidth - Width) / 2);  
  248.                     y += ((ImageHeight - Height) / 2);  
  249.                     dc.DrawRectangle(new SolidColorBrush(Color.Black), new Pen(Color.Black), x + 2, y + 2, Width, Height);  
  250.                     dc.Bitmap.StretchImage(x, y, _p_w_picpath, Width, Height, 255);  
  251.                 }  
  252.             }  
  253.         }  
  254.     }  
  255. }  

本程序的源代码下载链接:http://www.sky-walker.com.cn/yefan/SourceCode/YFPresentation.rar
有条件的网友可以和微软提供的示例NewPresentation对比一下看看,也许更能看出它们的异同。有时间我在把这个类封装成一个控件(基于.Net CF),这样在windows和wince平台就都能使用了。