IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    WP8.1开发:UserControl 中页面导航的问题

    编程小梦发表于 2014-11-15 14:52:44
    love 0

    (最近忙于学服务器编程的知识,准备做一款社交应用.一直在学习服务器,数据库的知识,在搞下WP,最后应付下学校的课程,实验,作业,每天时间满满的.所以就没时间发文章了.)

    PS;周老师说他没看明白我的意思.所以我就在修改一遍,相关代码都贴出来.

    之前题为坑只是想说对我这种初学者来说比较容易出错,没有说这是WP系统问题的意思.所以把坑改为问题.

    今天和大家分享一下WP8.1开发:UserControl 中页面导航的一个问题

    WP8.1中如果你自定义了UserControl控件,则在UserControl的后台中使用Navigate导航时,是一个跨线程操作.

    (注:此问题在模拟器不存在,我是在WP8.1的预览版中存在这个问题,不知道WP8.1正式版有没有.)

    我的疑惑是UserControl中的一个导航事件为什么是跨线程的呢?是因为UserControl的逻辑是在后台线程,不在当前UI线程.然后当我们需要导航到一个新的页面时,就需要和UI线程通信,所以需要跨线程操作.

    起因是这样的,我自定义了一个布局控件.如图:

    其代码如下:(注:修改自<深入理解windows phone 8.1UI控件编程>)

    WP8.1自定义布局控件:

    public class CirclePanel : Panel
        {
            private double _radius = 0;
            public double X;
            public double Y;
    
            public CirclePanel()
            {
    
            }
            public static readonly DependencyProperty RadiusProperty = DependencyProperty.RegisterAttached
                ("Radius",
                typeof(double),
                typeof(CirclePanel),
                new PropertyMetadata(0.0, OnRadiusPropertyChanged));
    
            public double Radius
            {
                get { return (double)GetValue(RadiusProperty); }
                set { SetValue(RadiusProperty, value); }
            }
    
            private static void OnRadiusPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
            {
                CirclePanel target = (CirclePanel)obj;
                target._radius = (double)e.NewValue;
                target.InvalidateArrange();
            }
    
            protected override Size MeasureOverride(Size availableSize)
            {
    
                double maxElementWidth = 0;
    
                foreach (UIElement child in Children)
                {
                    if (child == Children.FirstOrDefault())
                    {
    
                    }
                    else
                    {
                        child.Measure(availableSize);
                        maxElementWidth = Math.Max(child.DesiredSize.Width, maxElementWidth);
                    }
    
                }
    
                double panelWidth = 2 * this.Radius + 2 * maxElementWidth;
                double width = Math.Min(panelWidth, availableSize.Width);
                double heigh = Math.Min(panelWidth, availableSize.Height);
                return new Size(width, heigh);
            }
    
            protected override Size ArrangeOverride(Size finalSize)
            {
                double degree = 0;
                double degreeStep = (double)360 / (this.Children.Count-1);
    
                double mX = this.DesiredSize.Width / 2;
                double mY = this.DesiredSize.Height / 2;
                X = mX;
                Y = mY;
    
                foreach (UIElement child in Children)
                {
                    if (child==Children.FirstOrDefault())
                    {
                        child.Arrange(new Rect(mX - this._radius, mY - this._radius, this._radius*2 , this._radius*2));
    
                    }
                    else
                    {
                        double angle = Math.PI * degree / 180.0;
                        double x = Math.Cos(angle) * this._radius;
                        double y = Math.Sin(angle) * this._radius;
                        RotateTransform rotateTransform = new RotateTransform();
                        rotateTransform.Angle = degree;
                        rotateTransform.CenterX =0;
                        rotateTransform.CenterY =0;
                        child.RenderTransform = rotateTransform;
    
                        child.Arrange(new Rect(mX + x, mY + y, child.DesiredSize.Width, child.DesiredSize.Height));
    
                        degree += degreeStep;
                    }
    
                }
    
                return finalSize;
            }

    是不是有些怪异,不过转起来蛮不错的,有风车的感觉.这种感觉当然需要动画来实现.单独这个控件当然没什么问题.当我这个布局控件移植到MainPage页面的HUB控件的 下时.问题,就来了.UI代码如下:

    
             
            
               
                
                    
                
                
                
                
                
                
                  
               
              
           

    后台代码如下:

    private void circlePanel_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
            {
                rotateTransform.CenterX = circlePanel.X;
                rotateTransform.CenterY = circlePanel.Y;
    
                storyboard.Begin();
                e.Complete();
            }
    
            private async void Button_Click(object sender, RoutedEventArgs e)
            {
                await new MessageDialog("你点我了!").ShowAsync();
            }

    此时 我们就无法在 MainPage .xaml.cs文件中直接通过name属性访问到circlePanel和rotateTransform.因为local:CirclePanel控件在DataTemplate中.

    (当然我们可以通过CirclePanel的load事件或者遍历可视化树方法在DataTemplate中获取CirclePanel控件.)

     

    我们无法获得circlePanel和rotateTransform.那么自然也无法通过触摸事件启动动画了.

    我就想到通过UserControl来解决.将风车控件的触摸事件封装到它本身,就可以解决这个问题.

    于是便自定义了一个UserControl.其代码如下:

    WP8.1通过UserControl自定义控件:

    
    
        
            
            
    
            
                
                
            
    
        
        
            
                
                    
                
                
                
                
                
                
    
            
        
    

    后台:

    private void circlePanel_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
            {
                rotateTransform.CenterX = circlePanel.X;
                rotateTransform.CenterY = circlePanel.Y;
    
                storyboard.Begin();
                e.Complete();
            }
    
            private async void transfer_Click(object sender, RoutedEventArgs e)
            {
    
               Frame frame = Window.Current.Content as Frame;
               await Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                       frame.Navigate(typeof(BusTransfer));
                   });
    
            }

    可是问题又来了.在风车控件中的4个小轮子我需要导航到其他页面.这也就是我要说明的坑.我开始导航代码是这样写的:

    Frame frame = Window.Current.Content as Frame;
    
     frame.Navigate(typeof(BusTransfer));

    但是当点击按钮导航时会直接闪退出来.还没有错误提示.

    于是我单步调试,依然没有错误提示!我彻底郁闷了!试了几次都没有错误提示输出.

    后来在微博朋友:JustinAndDesign 的帮助下:

    我们可以使用: 当找不到exceptions的时候可以尝试在VS里找到Exceptions (Ctrl + Alt+ E) 然后在“Common Language Runtime Excpetions”对应的Thrown那里打勾.于是乎异常终于抛出来了:

    提示如下:

    The current thread is not currently running code or the call stack could not be obtained

    直接翻译:正在运行的代码没在当前线程上或者无法获得回调栈.

    WP8.1后台线程和UI线程:

    意思就是我们当前没有在UI线程,而是在后台线程.所以我们需要使用.Dispatcher.RunAsync方法使得我们的后台线程可以和当前UI线程通信.(PS:WP中线程有俩类:后台线程和UI线程,其中UI线程是我们应用的主线程.UI线程只有一个,但是后台线程可以有多个.)

    Frame frame = Window.Current.Content as Frame;
                await Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    frame.Navigate(typeof(BusStation));
                });

    改了N次啊!

    如果小梦的文章对你有帮助!欢迎支持小梦!



沪ICP备19023445号-2号
友情链接