• 2008-04-28

    [Code]Silverlight2鼠标右键屏蔽与使用 - [Code]

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/sgzxy-logs/19948488.html

          网上已经流传一个SL 1.0(JS)制作鼠标右键菜单的Demo,不过这个功能在SL 2的托管代码中还是有些不同的,估计高手们都能自理(疑惑的是除了国外有一篇相关文章和Demo外,还没找到国内的好文章,而国外的那个弄得功能太多,不够简明),但是菜鸟无疑是要摸索的……我参考了几份信息再摸索试验一下解决了问题,在此给出一个简明的教程:

          要做的事情其实只有两件:1.屏蔽掉那个烦人的右键“Silverlight Configuration”响应 2.将Html中的oncontextmenu事件托管给代码来处理

          第一步:修改加载SL控件的Html文件,如果是SL 2的话应该能找到如下的语句:

          <div id="silverlightControlHost">
          <object data="data:application/x-silverlight," type="application/x-silverlight-2-b1" width="100%" height="100%">
       <param name="source" value="ClientBin/WeShot Beta1.xap"/>
       <param name="onerror" value="onSilverlightError" />
       <param name="background" value="black" />

       ...

          现在进行两处的修改,修改的结果如下:

              <div id="silverlightControlHost"  oncontextmenu="window.event.returnValue=false">
              <object data="data:application/x-silverlight," type="application/x-silverlight-2-b1" width="100%" height="100%">
       <param name="source" value="ClientBin/WeShot Beta1.xap"/>
       <param name="onerror" value="onSilverlightError" />
       <param name="background" value="black" />
       <param name="windowless" value="true" />

         可见变化的是第一行和最后一行。第一行声明的oncontextmenu属性(VS Html编辑器会有警告,不用理)通过执行JS代码返回false来达到屏蔽右键默认菜单的功能。最后一行修改SL2控件的windowless属性(此属性当控件实例化完成后将不可在代码中修改)为true,这样使oncontextmenu事件能被后台代码所用。

         第二步,其实实质上只需要托管代码中的一条语句(C#),即:

         HtmlPage. Document. AttachEvent ( "oncontextmenu" , MouseRightButtonHandler );

         第二个参数是自定义的EventHandler<HtmlEventArgs>委托名。然后在程序中再按鼠标右键时就会执行你定义的委托。然而这样未免太简陋了,所以我做了个MouseRightButtonHelper类的简单封装,仅供菜鸟们参考:

    public class MouseRightButtonHelper
        {
            public event MouseEventHandler MouseRightButtonDown;
            private bool _isMouseOver = false;
            private MouseEventArgs _mouseInfo;
            private FrameworkElement _body;

            public MouseRightButtonHelper ( FrameworkElement element )
            {
                if ( HtmlPage. IsEnabled )
                {
                    HtmlPage. Document. AttachEvent ( "oncontextmenu" , MouseRightButtonHandler );
                }

                element. MouseEnter += HandleMouseEnter;
                element. MouseLeave += HandleMouseLeave;
                element. MouseMove += HandleMouseMove;
                _body = element;
            }

            private void MouseRightButtonHandler ( object sender , HtmlEventArgs e )
            {
                if (_isMouseOver&&MouseRightButtonDown !=null)
                    MouseRightButtonDown ( _body , _mouseInfo );
            }

            private void HandleMouseMove ( object sender , MouseEventArgs e )
            {
                _isMouseOver = true;
                _mouseInfo = e;
            }

            private void HandleMouseLeave ( object sender , MouseEventArgs e )
            {
                _isMouseOver = false;
            }

            private void HandleMouseEnter ( object sender , MouseEventArgs e )
            {
                _isMouseOver = true;
            }
        }

        使用时只需诸如:new MouseRightButtonHelper( 需要绑定右键事件的元素).MouseRightButtonDown += 某MouseEventHandler委托(注意不是MouseButtonEventHandler)   的方式即可

          嘿嘿至此,SL2关于鼠标支持的缺陷基本全部被克服,包括鼠标滚轮、多种鼠标按键检测等。其中我封装了一个MouseEventHelper类,利用System.Threading.Timer类的多线程功能,较完美地实现了包括拖拽、单击、双击、双击拖拽几种鼠标事件的识别。等以后有空再贴出来罢。

    (关于以上方法带来的问题请一定参考另一篇文章:http://www.blogbus.com/sgzxy-logs/20784735.html

          下面贴出自己做的MouseEventHelper类,支持滚轮、双击、单击+Press等几个事件的检测,需要加载System.Threading命名空间。这个是在一个新项目中重新设计的,只进行过简单的测试,不能保证没有bug,而且现在没有文档……所以仅供学习参考吧:

        public class MouseEventHelper: IDisposable
        {
            #region Properties

            #endregion

            #region Public Methods
            public MouseEventHelper( UIElement sender, bool needMouseWheel )
            {
                _bindObj = sender;
                _needMouseWheel = needMouseWheel;
                _bindObj.MouseLeftButtonDown += new MouseButtonEventHandler(_bindObj_MouseLeftButtonDown);
                _bindObj.MouseLeftButtonUp += new MouseButtonEventHandler(_bindObj_MouseLeftButtonUp);
                _bindObj.MouseMove += new MouseEventHandler(_bindObj_MouseMove);
                if(_needMouseWheel)
                {
                    _bindObj.GotFocus += new RoutedEventHandler(_bindObj_GotFocus);
                    _bindObj.LostFocus += new RoutedEventHandler(_bindObj_LostFocus);
                    _bindObj.MouseEnter += new MouseEventHandler(_bindObj_MouseEnter);
                    _bindObj.MouseLeave += new MouseEventHandler(_bindObj_MouseLeave);
                }
            }

            public void Dispose()
            {
                _bindObj.MouseLeftButtonDown -= _bindObj_MouseLeftButtonDown;
                _bindObj.MouseLeftButtonUp -= _bindObj_MouseLeftButtonUp;
                if(_needMouseWheel)
                {
                    _bindObj.GotFocus -= _bindObj_GotFocus;
                    _bindObj.LostFocus -= _bindObj_LostFocus;
                    _bindObj.MouseEnter -= _bindObj_MouseEnter;
                    _bindObj.MouseLeave -= _bindObj_MouseLeave;
                }
            }

            #endregion

            #region Private Methods
            static MouseEventHelper()
            {
                if(HtmlPage.IsEnabled)
                {
                    HtmlPage.Window.AttachEvent("DOMMouseScroll", MouseWheelEventHandler);
                    HtmlPage.Window.AttachEvent("onmousewheel", MouseWheelEventHandler);
                    HtmlPage.Document.AttachEvent("onmousewheel", MouseWheelEventHandler);
                }
            }

            static void MouseWheelEventHandler( object sender, HtmlEventArgs e )
            {
                double delta = 0;
                ScriptObject eventObj = e.EventObject;
                if(eventObj.GetProperty("wheelDelta") != null)
                {
                    delta = ((double)eventObj.GetProperty("wheelDelta")) / 120;
                    if(HtmlPage.Window.GetProperty("opera") != null)        //For Opera Browser
                        delta = -delta;
                }
                else if(eventObj.GetProperty("detail") != null)
                {
                    delta = -((double)eventObj.GetProperty("detail")) / 3;
                    if(HtmlPage.BrowserInformation.UserAgent.IndexOf("Macintosh") != -1)         //For Apple OS
                        delta = delta * 3;
                }

                if(delta != 0 && _currentMouseWheelFocus != null && _currentMouseWheelFocus.MouseWheelRoll != null)
                {
                    _currentMouseWheelFocus.RaiseMouseWheelRoll(_currentMouseWheelFocus._bindObj, new MouseWheelEventArgs(delta));
                    e.PreventDefault();
                }
            }

            #region Event Handlers
            void _bindObj_MouseLeftButtonDown( object sender, MouseButtonEventArgs e )
            {
                switch(_mouseButtonState)
                {
                    case START:
                        _bindObj.CaptureMouse();
                        _mouseButtonState = FIRST_DOWN;
                        StartTimer(CheckQuickClick, QUICK_CLICK_TIME );
                        _tempArgs = e;
                        break;
                    case QUICK_CLICK:
                        EndTimer();
                        _mouseButtonState = SECOND_DOWN;
                        StartTimer(CheckDoubleClick, DOUBLE_CLICK_TIME);
                        break;
                    default:
                        EndTimer();
                        EndAll();
                        break;
                }
            }
            void _bindObj_MouseLeftButtonUp( object sender, MouseButtonEventArgs e )
            {
                switch(_mouseButtonState)
                {
                    case FIRST_DOWN:
                        EndTimer();
                        _mouseButtonState = QUICK_CLICK;
                        StartTimer(CheckSecondDown, SECOND_CLICK_WAIT_TIME );
                        break;
                    case FIRST_DOWN_TIMEOVER:
                        EndAll();
                        RaiseMouseLeftButtonClick(_bindObj, _tempArgs);
                        break;
                    case SECOND_DOWN:
                        EndTimer();
                        EndAll();
                        RaiseMouseLeftButtonDoubleClick(_bindObj, e);
                        break;
                    case SECOND_DOWN_TIMEOVER:
                        EndAll();
                        break;
                    case MOUSE_DRAG:
                        EndAll();
                        RaiseMouseLeftButtonDragCompleted(_bindObj, e);
                        break;
                    case MOUSE_CLICK_DRAG:
                        EndAll();
                        RaiseMouseLeftButtonClickDragCompleted(_bindObj, e);
                        break;
                    default :
                        EndTimer();
                        EndAll();
                        break;
                }
            }
            void _bindObj_MouseMove( object sender, MouseEventArgs e )
            {
                if(_mouseButtonState != START)
                {
                    if(_mouseButtonState == MOUSE_DRAG)
                        RaiseMouseLeftButtonDrag(_bindObj, e);
                    else if(_mouseButtonState == MOUSE_CLICK_DRAG)
                        RaiseMouseLeftButtonClickDrag(_bindObj, e);
                    else
                    {
                        switch(_mouseButtonState)
                        {
                            case FIRST_DOWN_TIMEOVER:
                            case FIRST_DOWN:
                                EndTimer();
                                RaiseMouseLeftButtonPress(_bindObj, _tempArgs);
                                _mouseButtonState = MOUSE_DRAG;
                                break;
                            case SECOND_DOWN_TIMEOVER:
                            case SECOND_DOWN:
                                EndTimer();
                                RaiseMouseLeftButtonClickPress(_bindObj, _tempArgs);
                                _mouseButtonState = MOUSE_CLICK_DRAG;
                                break;
                        }
                    }
                }
            }

            void _bindObj_LostFocus( object sender, RoutedEventArgs e )
            {
                _currentMouseWheelFocus = this;
            }
            void _bindObj_GotFocus( object sender, RoutedEventArgs e )
            {
                _currentMouseWheelFocus = null;
            }
            void _bindObj_MouseLeave( object sender, MouseEventArgs e )
            {
                _isMouseOnObj = false;
            }
            void _bindObj_MouseEnter( object sender, MouseEventArgs e )
            {
                _isMouseOnObj = true;
            }
            #endregion

            #region Timer Callbacks
            void CheckQuickClick( object state )
            {
                EndTimer();
                if(_mouseButtonState == FIRST_DOWN)
                {
                    _mouseButtonState = FIRST_DOWN_TIMEOVER;
                    Global.RootCanvas.Dispatcher.BeginInvoke(delegate
                    {
                        RaiseMouseLeftButtonPress(_bindObj, _tempArgs);
                    });
                }
            }
            void CheckSecondDown( object state )
            {
                EndTimer();
                if(_mouseButtonState == QUICK_CLICK)
                {
                    Global.RootCanvas.Dispatcher.BeginInvoke(delegate
                    {
                        EndAll();
                        RaiseMouseLeftButtonClick(_bindObj, _tempArgs);
                    });
                }
            }
            void CheckDoubleClick( object state)
            {
                EndTimer();
                if(_mouseButtonState == SECOND_DOWN)
                {
                    _mouseButtonState = SECOND_DOWN_TIMEOVER;
                    Global.RootCanvas.Dispatcher.BeginInvoke(delegate
                    {
                        RaiseMouseLeftButtonClickPress(_bindObj, _tempArgs);
                    });
                }
            }
            #endregion

            #region Event Raisers
            void RaiseMouseLeftButtonPress( object sender, MouseButtonEventArgs e )
            {
                if(MouseLeftButtonPress != null)
                    MouseLeftButtonPress(sender, e);
            }
            void RaiseMouseLeftButtonDragCompleted( object sender, MouseButtonEventArgs e )
            {
                if(MouseLeftButtonDragCompleted != null)
                    MouseLeftButtonDragCompleted(sender, e);
            }
            void RaiseMouseLeftButtonClickPress( object sender, MouseButtonEventArgs e )
            {
                if(MouseLeftButtonClickPress != null)
                    MouseLeftButtonClickPress(sender, e);
            }
            void RaiseMouseLeftButtonClickDragCompleted( object sender, MouseButtonEventArgs e )
            {
                if(MouseLeftButtonClickDragCompleted != null)
                    MouseLeftButtonClickDragCompleted(sender, e);
            }
            void RaiseMouseLeftButtonClick( object sender, MouseButtonEventArgs e )
            {
                if(MouseLeftButtonClick != null)
                    MouseLeftButtonClick(sender, e);
            }
            void RaiseMouseLeftButtonDoubleClick( object sender, MouseButtonEventArgs e )
            {
                if(MouseLeftButtonDoubleClick != null)
                    MouseLeftButtonDoubleClick(sender, e);
            }
            void RaiseMouseLeftButtonDrag( object sender, MouseEventArgs e )
            {
                if(MouseLeftButtonDrag != null)
                    MouseLeftButtonDrag(sender, e);
            }
            void RaiseMouseLeftButtonClickDrag( object sender, MouseEventArgs e )
            {
                if(MouseLeftButtonClickDrag != null)
                    MouseLeftButtonClickDrag(sender, e);
            }

            void RaiseMouseWheelRoll( object sender, MouseWheelEventArgs e )
            {
                if(MouseWheelRoll != null)
                    MouseWheelRoll(sender, e);
            }
            #endregion

            #region Helper Functions
            void StartTimer( TimerCallback callback, int millionseconds )
            {
                _timer = new Timer(callback, null, millionseconds, 0);
            }
            void EndTimer()
            {
                _timer.Dispose();
            }

            void EndAll()
            {
                _bindObj.ReleaseMouseCapture();
                _mouseButtonState = START;
            }
            #endregion
            #endregion

            #region Constants
            const int START = 0,
                FIRST_DOWN = 1,
                FIRST_DOWN_TIMEOVER = 2,
                QUICK_CLICK = 4,
                SECOND_DOWN = 8,
                SECOND_DOWN_TIMEOVER = 16,
                MOUSE_DRAG = 32,
                MOUSE_CLICK_DRAG = 64;
            const int QUICK_CLICK_TIME = 90, DOUBLE_CLICK_TIME = 100, SECOND_CLICK_WAIT_TIME = 200;
            #endregion

            #region Private Variables
            static MouseEventHelper _currentMouseWheelFocus = null;
            static int _mouseButtonState;
            static Timer _timer;
            static MouseButtonEventArgs _tempArgs;

            UIElement _bindObj;
            bool _isMouseOnObj, _needMouseWheel;

            #endregion

            #region Events
            public event MouseButtonEventHandler MouseLeftButtonPress;
            public event MouseEventHandler MouseLeftButtonDrag;
            public event MouseButtonEventHandler MouseLeftButtonDragCompleted;
            public event MouseButtonEventHandler MouseLeftButtonClick;

            public event MouseButtonEventHandler MouseLeftButtonClickPress;
            public event MouseEventHandler MouseLeftButtonClickDrag;
            public event MouseButtonEventHandler MouseLeftButtonClickDragCompleted;
            public event MouseButtonEventHandler MouseLeftButtonDoubleClick;

            public event EventHandler<MouseWheelEventArgs> MouseWheelRoll;
            #endregion
        }

        public class MouseWheelEventArgs: EventArgs
        {
            private double _delta;
            public MouseWheelEventArgs( double delta )
            {
                this._delta = delta;
            }
            public double Delta
            {
                get
                {
                    return this._delta;
                }
            }
        }

    分享到:

    评论

  • 楼主,那个
    Global.RootCanvas.Dispatcher.BeginInvoke
    是将Mouse事件绑定到最底层的Canvas上面么?
    回复gemini说:
    噢Sorry,那个Global.RootCanvas是项目相关的,真不好意思忘了,这里需要改一下的……因为这个Helper类用了多线程的Timer,所以当Timer到时间需要触发事件,必须要跨线程调用UI线程的东西,所以必须找到一个全局的UI线程的对象来使用Dispatcher……
    可以为这个Helper类增加一个静态的UIElement成员,并在使用Helper对象前初始化这个静态成员(一个全局的UI元素)。然后将需要用到Dispatcher的地方都换成这个属性。
    之所以使用多线程的Timer而非单线程的DispatcherTimer,是因为后者经过实验,发现在性能比较差的机器上效果很不好,鼠标检测基本失去功能(因为单线程造成计时器时间严重不准)。
    2009-03-30 14:39:50
  • 额,谢谢楼主回复,
    SL3里面MS似乎也没打算支持右键,
    其实右键的实现是靠windowless实现的,对吧,
    这个东西据说开销大,且仅适用windows版本,
    麻烦啊。
    楼主有gmail么?指教一下。谢谢啦。
    回复gemini说:
    没错,而且那个windowless会同时屏蔽掉IME输入法,中文无法输入……
    我的Email是:sanguoxiangying@sina.com
    2009-03-30 14:43:18
  • 楼主提供一下你的MouseEventHelper啊
    回复gemini说:
    嗯已提供,不过
    1、并不支持鼠标右键,因为在SL2中屏蔽鼠标右键会带来N多不可调和的问题;有兴趣可以探索下SL3中是否能用上鼠标右键
    2、不保证没bug
    所以仅供参考吧……
    2009-03-26 11:25:47