Silverlight开发中的疑难杂症-如何为Silverlight添加默认按钮

  在开发中发现Silverlight中没有WPFDefaultButtonCancelButton属性,于是就准备自己实现一个。DefaultButton要实现的是在按下Enter键的时候触发对应的按钮事件,而CancelButton要实现的是在按下Esc键的时候触发对应的按钮事件。本以为是个很简单的事情,只需要在对应的keyup事件中进行按键判断,然后调用对应的按钮事件即可。结果在实现时发现,Silverlight中并没有WPF中常用的RaiseEvent方法,一时间无从下手,最后只好再次求助谷歌(没有它我怎么活啊~)。最终再几种方案中选择了一种比较合适的跟大家分享一下。它通过UI自动化中的ButtonAutomationPeer类来完成了按钮的模拟点击,为了能够方便以后的使用,这里还是将其实现成了一个扩展方法,具体的代码如下:

ButtonExtension
///   <summary>
///  按钮扩展类
///   </summary>
public   static   class  ButtonExtension
{
    
///   <summary>
    
///  点击按钮
    
///   </summary>
    
///   <param name="clickBtn"></param>
     public   static   void  PerfomClick( this  Button clickBtn)
    {
        
// 这里必须按判断是否处于Enable状态
         if  (clickBtn  !=   null   &&  clickBtn.IsEnabled)
        {
            
// 通过代码点击按钮
            var peer  =   new  ButtonAutomationPeer(clickBtn);
            var invokeProv 
=  peer.GetPattern(PatternInterface.Invoke)  as  IInvokeProvider;
            
if  (invokeProv  !=   null )
            {
                invokeProv.Invoke();
                
// 调用后同时设置焦点为按钮
                clickBtn.Focus();
            }
        }
    }
}

完成了最为关键的部分,接下来就是默认按钮以及撤销按钮的实现,这里还是通过附加属性来实现这一个功能,关于附加属性的具体使用请见我的 这篇文章 。实现的思路就是注册属性所附加到的元素的KeyUp事件,在EnterEsc键按下时调用按钮的点击事件。完整的代码如下:

ButtonServices
///   <summary>
///  SilverLight按钮服务
///   </summary>
public   class  ButtonServices
{
    
#region  Dependency Properties

    
#region  默认按钮

    
#region  DefaultButton

    
public   static  Button GetDefaultButton(DependencyObject obj)
    {
        
return  (Button)obj.GetValue(DefaultButtonProperty);
    }

    
public   static   void  SetDefaultButton(DependencyObject obj, Button value)
    {
        obj.SetValue(DefaultButtonProperty, value);
    }

    
public   static   readonly  DependencyProperty DefaultButtonProperty  =
        DependencyProperty.RegisterAttached(
" DefaultButton " typeof (Button),  typeof (ButtonServices),  new  PropertyMetadata(OnDefaultButtonChanged));

    
#endregion

    
private   static   void  OnDefaultButtonChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        UIElement scopeElement 
=  obj  as  UIElement;
        
if  (scopeElement  !=   null )
        {
            scopeElement.KeyUp 
+=   new  KeyEventHandler(Element_EnterKeyUp);
        }
    }

    
static   void  Element_EnterKeyUp( object  sender, KeyEventArgs e)
    {
        
if  (e.Key  ==  Key.Enter)
        {
            UIElement scopeElement 
=  sender  as  UIElement;
            
if  (scopeElement  !=   null )
            {
                Button defaultButton 
=  scopeElement.GetValue(ButtonServices.DefaultButtonProperty)  as  Button;
                
// 通过代码点击按钮
                defaultButton.PerfomClick();
            }
        }
    }

    
#endregion

    
#region  取消按钮

    
#region  CancelButton

    
public   static  Button GetCancelButton(DependencyObject obj)
    {
        
return  (Button)obj.GetValue(CancelButtonProperty);
    }

    
public   static   void  SetCancelButton(DependencyObject obj, Button value)
    {
        obj.SetValue(CancelButtonProperty, value);
    }

    
public   static   readonly  DependencyProperty CancelButtonProperty  =
        DependencyProperty.RegisterAttached(
" CancelButton " typeof (Button),  typeof (ButtonServices),  new  PropertyMetadata(OnCancelButtonChanged));

    
#endregion

    
private   static   void  OnCancelButtonChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        UIElement scopeElement 
=  obj  as  UIElement;
        
if  (scopeElement  !=   null )
        {
            scopeElement.KeyUp 
+=   new  KeyEventHandler(Element_EscKeyUp);
        }
    }

    
static   void  Element_EscKeyUp( object  sender, KeyEventArgs e)
    {
        
if  (e.Key  ==  Key.Escape)
        {
            UIElement scopeElement 
=  sender  as  UIElement;
            
if  (scopeElement  !=   null )
            {
                Button clickBtn 
=  scopeElement.GetValue(ButtonServices.DefaultButtonProperty)  as  Button;
                
// 通过代码点击按钮
                clickBtn.PerfomClick();
            }
        }
    }

    
#endregion

    
#endregion
}

接下来只要将这个属性附加到要作用的元素下就可以实现默认按钮和撤销按钮的效果啦,使用时先添加如下别名,如下:

xmlns:MyExtensions="clr-namespace:YQL.Core.Extensions;assembly=YQL.Core.Silverlight"

<Grid x:Name="LayoutRoot" MyExtensions:ButtonServices.DefaultButton="{Binding ElementName=btnLogin}"/>

PS:做完之后想到一个问题,在附加属性中注册事件,很难找到合适的时机取消事件的注册,这样就可能会引起内存泄露,不知道这里除了WeakEvent Pattern外还有什么方法能够简单的防治内存的泄露。关于WeakEvent Pattern大家可以参考MSDN的文章 http://msdn.microsoft.com/en-us/library/aa970850.aspx

你可能感兴趣的:(silverlight)