none
ScrollViewer控件会吃掉MouseLeftButtonDown事件而不在向上冒泡吗? RRS feed

  • 问题

  • 郁闷死了。碰到这样一个问题,如下面的xaml描述所示,一个Canvas套了一个ScrollViewer,当这个ScrollViewer中的Canvas 捕获到MouseLeftButtonDown鼠标事件时,按理说它会在处理完成后冒泡给parent Canvas 继续处理。郁闷的是parentCanvas 根本就没有收到这个MouseLeftButtonDown事件。但又不是完全屏蔽,比如能收到ScrollViewer包含的Canvas传递上来的MouseLeftButtonUp事件。这是为什么呀。如果ScrollViewer确实会阻值冒泡,那么为什么单单阻值Down而放过Up事件?

     

    真是让人费解呀。。。

     

     

    难道是我孤陋寡闻,还是有其他原因。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

     

    Page.xaml内容如下

    Code Snippet
        <Canvas x:Name="LayoutRoot" Background="White" MouseLeftButtonDown="LayoutRoot_MouseLeftButtonDown" MouseLeftButtonUp="LayoutRoot_MouseLeftButtonUp">
            <ScrollViewer Width="300">
                <Canvas MouseLeftButtonDown="Canvas_MouseLeftButtonDown">
                    <Rectangle Width="50" Height="50" Fill="Red"/>
                </Canvas>
            </ScrollViewer>
        </Canvas>

     

     

     

    Page.xaml.cs内容如下

    Code Snippet

        public partial class Page : UserControl
        {
            public Page()
            {
                InitializeComponent();
            }

            private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                Debug.WriteLine("=======Canvas Mouse Down==========");
            }

            private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                Debug.WriteLine("=======LayoutRoot Mouse Down==========");
            }

            private void LayoutRoot_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                Debug.WriteLine("=======LayoutRoot Mouse Up==========");
            }
        }

     

     

    2008年9月12日 8:12

答案

  • 这里其实有两个问题,因为Silverlight 2.0还是Beta,估计这里的事件冒泡还是有一定BUG,偶尔我也会遇到,但是,只要正确释放了鼠标的捕获是能在通常情况下正常工作,除非有某些极端情况出现,如果对于这种情况比较敏感,建议参考一下我博客里的有关UI线程的文章:http://hi.baidu.com/zhenyk/blog/ria ,尽量采用队列的方式手动把每个委托传递出去,这样处理的时候就不会有漏掉的情况,给你个例子:

             #region 后台辅助线程定义和事件监听声明
                bw.WorkerReportsProgress = true;
                bw.WorkerSupportsCancellation = true;
                bw.DoWork += new DoWorkEventHandler(bw_DoWork);
                bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
                bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

             #endregion

     

    #region 后台线程相关事件处理函数
            private static Stack<Delegate> allWorks = new Stack<Delegate>();
            private static void bw_OutDoWork(Delegate workhandle)
            {
                if (bw.IsBusy != true)
                {
                    bw.RunWorkerAsync(workhandle);
                }
                else
                {
                    lock (allWorks)
                    {
                        allWorks.Push(workhandle);
                    }
                }
            }

            private void bw_OutCancleWork()
            {
                if (bw.WorkerSupportsCancellation == true)
                {
                    bw.CancelAsync();
                }
            }
            delegate void myD();
            private void bw_DoWork(object sender, DoWorkEventArgs e)
            {
                BackgroundWorker worker = sender as BackgroundWorker;


                if ((worker.CancellationPending == true))
                {
                    e.Cancel = true;
                    return;
                }
                else
                {
                    // Perform a time consuming operation and report progress.
                    myD myDelegate = (myD)e.Argument;
                    _thisbase.Dispatcher.BeginInvoke(new Action(myDelegate));
                 }

            }
            private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if (allWorks.Count > 0)
                {
                    lock (allWorks)
                    {
                        bw_OutDoWork(allWorks.Pop());
                    }
                }
            }

            private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                //this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
            }

            #endregion

     

     

    用法:

    bw_OutDoWork((myD)delegate
    {

        //这里是你想做的事情,可以是一些语句例如:

        System.Windows.Browser.HtmlPage.window.alert("Shadow");

    }

     

    这个思路就是把你每次需要执行的代码压寨来保证和UI线程之间不会产生冲突(捕获MouseEvent的线程)

     

    其实,实际情况可能要更复杂一些,但是,只要正确使用事件就应该信任.net不会出错,就算有错也会被及时ServicePack的。

    2008年9月17日 6:23

全部回复

  •  

    可以,但是需要释放鼠标的捕获,理论上,如果事件被触发,鼠标会持续在被捕获状态,我测试了一下,事件能都正常冒泡出来:

    private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                Debug.WriteLine("=======Canvas Mouse Down==========");
                ((Canvas)sender).ReleaseMouseCapture();
            }

            private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                Debug.WriteLine("=======LayoutRoot Mouse Down==========");
                ((Canvas)sender).ReleaseMouseCapture();
            }

            private void LayoutRoot_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                Debug.WriteLine("=======LayoutRoot Mouse Up==========");
                ((Canvas)sender).ReleaseMouseCapture();
            }

    2008年9月12日 10:38
  •  

    谢谢楼上的回复。不过我用了您的方法Scrollview中Canvas的MouseDown事件仍然不能冒泡到LayoutRoot对象。但是MouseUp事件却能正确的冒泡。我的是最新的beta2版,不知是否是版本问题。

    2008年9月16日 2:10
  • 这里其实有两个问题,因为Silverlight 2.0还是Beta,估计这里的事件冒泡还是有一定BUG,偶尔我也会遇到,但是,只要正确释放了鼠标的捕获是能在通常情况下正常工作,除非有某些极端情况出现,如果对于这种情况比较敏感,建议参考一下我博客里的有关UI线程的文章:http://hi.baidu.com/zhenyk/blog/ria ,尽量采用队列的方式手动把每个委托传递出去,这样处理的时候就不会有漏掉的情况,给你个例子:

             #region 后台辅助线程定义和事件监听声明
                bw.WorkerReportsProgress = true;
                bw.WorkerSupportsCancellation = true;
                bw.DoWork += new DoWorkEventHandler(bw_DoWork);
                bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
                bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

             #endregion

     

    #region 后台线程相关事件处理函数
            private static Stack<Delegate> allWorks = new Stack<Delegate>();
            private static void bw_OutDoWork(Delegate workhandle)
            {
                if (bw.IsBusy != true)
                {
                    bw.RunWorkerAsync(workhandle);
                }
                else
                {
                    lock (allWorks)
                    {
                        allWorks.Push(workhandle);
                    }
                }
            }

            private void bw_OutCancleWork()
            {
                if (bw.WorkerSupportsCancellation == true)
                {
                    bw.CancelAsync();
                }
            }
            delegate void myD();
            private void bw_DoWork(object sender, DoWorkEventArgs e)
            {
                BackgroundWorker worker = sender as BackgroundWorker;


                if ((worker.CancellationPending == true))
                {
                    e.Cancel = true;
                    return;
                }
                else
                {
                    // Perform a time consuming operation and report progress.
                    myD myDelegate = (myD)e.Argument;
                    _thisbase.Dispatcher.BeginInvoke(new Action(myDelegate));
                 }

            }
            private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if (allWorks.Count > 0)
                {
                    lock (allWorks)
                    {
                        bw_OutDoWork(allWorks.Pop());
                    }
                }
            }

            private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                //this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
            }

            #endregion

     

     

    用法:

    bw_OutDoWork((myD)delegate
    {

        //这里是你想做的事情,可以是一些语句例如:

        System.Windows.Browser.HtmlPage.window.alert("Shadow");

    }

     

    这个思路就是把你每次需要执行的代码压寨来保证和UI线程之间不会产生冲突(捕获MouseEvent的线程)

     

    其实,实际情况可能要更复杂一些,但是,只要正确使用事件就应该信任.net不会出错,就算有错也会被及时ServicePack的。

    2008年9月17日 6:23