阅读内容 

在silverlight中定制自己的MessageBox(消息框)

[日期:2008-08-28] 来源:  作者: [字体: ]
     在silverlight中,如果想使用“消息框”可使用下面的方法,即:HTMLPage.Window.Alert("消息框内容")。
  如果想要显示更加复杂的内容或定义消息框样式的话,基本上没有什么好的方法。最近在网上看到了一篇文章,
  该文章的作者也谈到了上面所说的话题,相关链接如下:  
   The Curious Incident of the MessageBox in the Silverlight App
  
   在线演示如下:http://silverlight.services.live.com/invoke/72193/messagebox/iframe.HTML
  
  
    首先是普通样式:
  
  
  
  接着是显示图形样式:
  
  使用新的样式风格:
  
  首先请下载本文中的源码(本人已部分修改了原文中的源码和相应的样式,以便进行DEMO演示)。下面是相
  
  应的类图和说明:
  
  接下来,将会以这个类图来逐个解释相应类结构信息,首先看一下MessageBoxControls(相应内容见注释):
  
  Code
  /// <summary>
  /// 消息框结果
  /// </summary>
  public enum MessageBoxResult
  {
   Yes,  //是
   No,   //否
   Cancel //取消
  }
  
  /// <summary>
  /// 消息事件参数
  /// </summary>
  public class MessageBoxResultEventArgs : EventArgs
  {
   public MessageBoxResult Result { get; set; }
   public object AsyncState { get; set; }
  }
  
  /// <summary>
  /// 消息框控件类,该模板包括三个组件(三个Button和一个Panel)
  /// </summary>
  [TemplatePart(Name = RootElement, Type = typeof(Panel))]
  [TemplatePart(Name = YesButtonElement, Type = typeof(Button))]
  [TemplatePart(Name = NoButtonElement, Type = (typeof(Button)))]
  [TemplatePart(Name = CancelButtonElement, Type = (typeof(Button)))]
  public class MessageBoxControl : ContentControl
  {
   public event EventHandler<MessageBoxResultEventArgs> MessageBoxDismissed;
  
   public MessageBoxControl()
   {
   DefaultStyleKey = typeof(MessageBoxControl);
   }
   public override void OnApplyTemplate()
   {
   #region 取消之前的事件绑定
  
   if (yesButton != null)
   {
   yesButton.Click -= OnYesButton;
   }
   if (noButton != null)
   {
   noButton.Click -= OnNoButton;
   }
   if (cancelButton != null)
   {
   cancelButton.Click -= OnCancelButton;
   }
  
   #endregion
  
   rootElement = base.GetTemplateChild(RootElement) as Panel;
   yesButton = base.GetTemplateChild(YesButtonElement) as Button;
   noButton = base.GetTemplateChild(NoButtonElement) as Button;
   cancelButton = base.GetTemplateChild(CancelButtonElement) as Button;
  
   #region 如果grid中有相应元素时,则绑定相应事件(详见下面的代码)
  
   if (yesButton != null)
   {
   yesButton.Click += OnYesButton;
   }
   if (noButton != null)
   {
   noButton.Click += OnNoButton;
   }
   if (cancelButton != null)
   {
   cancelButton.Click += OnCancelButton;
   }
  
   #endregion
   }
  
   void OnYesButton(object sender, EventArgs args)
   {
   FireDismissed(MessageBoxResult.Yes);
   }
   void OnNoButton(object sender, EventArgs args)
   {
   FireDismissed(MessageBoxResult.No);
   }
   void OnCancelButton(object sender, EventArgs args)
   {
   FireDismissed(MessageBoxResult.Cancel);
   }
  
   /// <summary>
   /// 调用绑定的事件,并传递相应参数
   /// </summary>
   /// <param name="result"></param>
   void FireDismissed(MessageBoxResult result)
   {
   //当绑定的事件不为空时(绑定部分参见MessageBox的构造函数)
   if (MessageBoxDismissed != null)
   {
   MessageBoxDismissed(this, new MessageBoxResultEventArgs() { Result = result });
   }
   }
  
   Button yesButton;
   Button noButton;
   Button cancelButton;
   Panel rootElement;
  
   #region 赋值信息参见generic.xaml中的"x:Name"声明
  
   public const string RootElement = "RootElement";
   public const string YesButtonElement = "YesButtonElement";
   public const string NoButtonElement = "NoButtonElement";
   public const string CancelButtonElement = "CancelButtonElement";
  
   #endregion
  }
   而MessageBox这个控件使用封装类结构如下(相关内容见注释):
  
  Code
  public class UserControlContentAccessor : UserControl
  {
   /// <summary>
   /// 获取当前UserControl的ContentProperty属性
   /// </summary>
   /// <param name="uc">当前UserControl</param>
   /// <returns>ContentProperty属性</returns>
   public static UIElement GetContent(UserControl uc)
   {
   return ((UIElement)uc.GetValue(UserControl.ContentProperty));
   }
   /// <summary>
   /// 设置当前UserControl的ContentProperty属性
   /// </summary>
   /// <param name="uc">当前UserControl</param>
   /// <param name="element">要设置的内容属性</param>
   public static void SetContent(UserControl uc, UIElement element)
   {
   uc.SetValue(UserControl.ContentProperty, element);
   }
  }
  
  /// <summary>
  /// 消息框类,该类可以看成是对"消息框控件类"使用封装(封装了事件绑定和内容信息)
  /// </summary>
  public static class MessageBox
  {
  /// <summary>
   /// 实际页面视图中的元素(用于当消息框关闭后,还原页面元素时使用)
   /// </summary>
   private static UIElement realVisual;
   /// <summary>
   /// 用于绑定当前页面中根元素节点
   /// </summary>
   private static Grid parentGrid;
   /// <summary>
   /// 状态值
   /// </summary>
   private static object asyncState;
   /// <summary>
   /// 用户绑定回调事件属性
   /// </summary>
   private static EventHandler<MessageBoxResultEventArgs> userCallback;
  
   public static void ShowAsync(object content)
   {
   ShowAsync(content, null);
   }
  
   public static void ShowAsync(object content,
   EventHandler<MessageBoxResultEventArgs> callback)
   {
   ShowAsync(content, null, callback);
   }
  
   public static void ShowAsync(object content, object userState,
   EventHandler<MessageBoxResultEventArgs> callback)
   {
   ShowAsync(content, userState, callback, null);
   }
  
   public static void ShowAsync(object content, object userState,
   EventHandler<MessageBoxResultEventArgs> callback, Style controlTemplate)
   {
   MessageBoxControl control = new MessageBoxControl();
   control.Content = content;
   //绑定指定样式
   if (controlTemplate != null)
   {
   control.Style = controlTemplate;
   }
   ShowAsync(control, userState, callback);
   }
   public static void ShowAsync(MessageBoxControl control, object userState,
   EventHandler<MessageBoxResultEventArgs> callback)
   {
   UserControl uc = Application.Current.RootVisual as UserControl;
  
   if (uc != null)
   {
   asyncState = userState;//用户状态绑定
   userCallback = callback;//回调方法
   realVisual = UserControlContentAccessor.GetContent(uc);
   realVisual.IsHitTestVisible = false; //使底层控件点击不可见
  
   parentGrid = new Grid();//声明一个Grid对象,用于加载新的内容
   UserControlContentAccessor.SetContent(uc, parentGrid);
  
   parentGrid.Children.Add(realVisual); //加载realVisual内容(注:此处内容中的控制已不支持点击了)
   parentGrid.Children.Add(control); //加载消息框实例,后加载的显示在上(前)面
  
   control.MessageBoxDismissed += OnDismissed; //绑定要处理的事件,该事件会在点击消息框中的"yes"或"no"按钮时执行
   }
   }
   static void OnDismissed(object sender, MessageBoxResultEventArgs e)
   {
  
   MessageBoxControl control = sender as MessageBoxControl;
  
   UserControl uc = Application.Current.RootVisual as UserControl;
  
   if (uc != null)
   {  //清除之前的页面UI元素,并还原页面初始时的元素设置
   parentGrid.Children.Clear();
   realVisual.IsHitTestVisible = true;
   UserControlContentAccessor.SetContent(uc, realVisual);
   }
   if (control != null)
   {
   control.MessageBoxDismissed -= OnDismissed;
   }
   try
   {
   if (userCallback != null)
   {
   //执行用户绑定的事件(并传递事件参数)
   userCallback(null, new MessageBoxResultEventArgs()
   {
   Result = e.Result,
   AsyncState = asyncState
   });
   }
   }
   finally
   {
   realVisual = null;
   parentGrid = null;
   asyncState = null;
   userCallback = null;
   }
   }
  }
   其实通过上面的类,我们可以看出作者是如何在当前页面中显示消息框信息的,也就是上面代码段里
  的如下代码:
  
  
   realVisual = UserControlContentAccessor.GetContent(uc);
   realVisual.IsHitTestVisible = false; //使底层控件点击不可见
  
   parentGrid = new Grid();//声明一个Grid对象,用于加载新的内容
   UserControlContentAccessor.SetContent(uc, parentGrid);
  
   parentGrid.Children.Add(realVisual); //加载realVisual内容(注:此处内容中的控制已不支持点击了)
   parentGrid.Children.Add(control); //加载消息框实例,后加载的显示在上(前)面
  
  
   也就是通过realVisual来保存原有的页面元素信息,然后重新按指定顺序(先realVisual再messagecontrol)
  加载UIElement来实现显示消息框的方式,当然这种有HACK味道的做法到底效果好不好,连原作者都表示怀疑,他
  本人也感觉还应有更好的Solution。
  
   当然realVisual变量的一个重要用处在于当消息框被关闭时,用它来还原页面中的元素,而这块代码就是上面
  所说的OnDismissed方法所做的事了,代码如下:
  
  OnDismissed (object sender, MessageBoxResultEventArgs e)
  {
  
   MessageBoxControl control = sender as MessageBoxControl;
  
   UserControl uc = Application.Current.RootVisual as UserControl;
  
   if (uc != null)
   {  //清除之前的页面UI元素,并还原页面初始时的元素设置
   parentGrid.Children.Clear();
   realVisual.IsHitTestVisible = true;
   UserControlContentAccessor.SetContent(uc, realVisual);
   }
  
  }
  
   这样,我们可以在应用程序中使用该类来显示相应的消息框了,其声明和使用代码如下:
  
  
  //普通样式
   void OnNormalClick(object sender, EventArgs args)
   {
   MessageBox.ShowAsync("简单调用, 无回调, 无状态, 无样式!");
   //下面注释的代码包括状态和回调事件 
   //MessageBox.ShowAsync("As previously but with a callback - hit NO", (s, e) =>
   // {
   // Debug.Assert(e.Result == MessageBoxResult.No);
   // });
  
   //MessageBox.ShowAsync("As previously but with state - hit YES", 101, (s, e) =>
   // {
   // Debug.Assert((e.Result == MessageBoxResult.Yes) && ((int)e.AsyncState == 101));
   // });
   }
  //显示图形
   void OnShapeClick(object sender, EventArgs args)
   {
   MessageBox.ShowAsync(new Ellipse()
   {
   Width = 80,
   Height = 80,
   Fill = new SolidColorBrush(Colors.Green)
   });
   }
  
   //转换样式
   void OnChangeStyleClick(object sender, EventArgs args)
   {
  
   Style myStyle = this.Resources["myStyle"] as Style;
  
   MessageBox.ShowAsync("使用一个不同的样式",
   101,//状态
   (s, e) => //处理事件
   {
   if (e.Result == MessageBoxResult.No && ((int)e.AsyncState == 101))
   {
   HTMLPage.Window.Alert("您点击了No按钮");
   }
  
   if (e.Result == MessageBoxResult.Yes)
   {
   HTMLPage.Window.Alert("您点击了Yes按钮");
   }
   },
   myStyle);
   }
  
   说到这里,还有一个内容没有介绍,也就是作者所定义的两个样式文件,其中之一被放置到了generic.xaml
  中,以便做了控制默认加载样式,其绑定直接在MessageBoxControl构造函数中完成,如下:
    
  public MessageBoxControl()
  {
   DefaultStyleKey = typeof(MessageBoxControl);
  }
   而另外的样式被放在了page.xaml中,以便于程序运行时访问,这里就不多作介绍了。不过本人已修改了这两
  个样式中的一些数值,主要是为了显示时比例更好看一些。
  
  
   好了,今天的内容就先到这里了。
  
   tag : silverlight,messagexbox
   作者: 代震军, daizhj
   原帖链接: http://files.cnblogs.com/daizhj/silverlight_MessageBox.rar
    
阅读:
录入:blue1000

推荐 】 【 打印
相关新闻      
本文评论       全部评论
发表评论
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款


点评: 字数
姓名:
Advertisement
内容查询


Advertisement