zoukankan      html  css  js  c++  java
  • Windows 8 Metro开发疑难杂症(一)——导航

    Win8的导航的实在是让我有点郁闷,尤其是像我这样原来做过WP7开发的真的一时难以适应。

    Win8导航的问题,目前一共有两个:

    1. 如果在不启用页面缓存的时候,每次返回的时候都会刷新页面(在WP7中页面的所有状态自动保存,离开的时候什么样的,返回的时候还是那样),这就导致用户状态需要开发者自己去保存。你可能会说既然这样可以启动页面缓存啊,是啊,你当然可以启用页面缓存,但是一旦启用了页面的缓存,你这个页面同一时间只能有一个实例,这样对于对应不同的参数显示不同的数据的页面来说就很困难了,因为你同一时间只有一个实例。
    2. 导航的时候用户是可以传递object参数的,我刚看到可以传递object参数的时候我还以开心,觉得微软终于把WP7的导航模型改进了下,可是当我调试挂起状态的时候我发现我错了,直接crash。于是我在微软的官网论坛问了下,给我的回答是,如果传递的参数是复杂类型(即使你已经标记了DataContract和DataMember)那么程序挂起的时候依然会crash,那也就是说你必须把你的复杂类型在传递之前序列化成字符串,然后在目标页把字符串反序列化成相应对象。我觉得微软你这么做有意思吗?尼玛你还不如用WP7导航模型啊!

     

    下面针对第一个问题进行讨论。首先如果你的页面不需要根据不同的参数显示不同的数据的,那么完全可以启用页面缓存(页面的NavigationCacheMode设置成Enabled或者Required)。如果你的页面需要根据不同的参数显示不同数据,那么作为开发者要做的事情比起WP7的导航模型来说要多了,首先是页面数据的保存,其次是页面状态的保存(如果有滚动条你得保存滚动条的状态,如果有文本框,你得保存文本框中的文本…..)。现在拿VS2012中的自带的模版做个列子。

    首先从项目模版中选择Grid APP模版,建立项目以后直接运行。进入首页,滑动滚动条,滑倒最后,然后随便选择一项进入该项详细信息页,然后返回,这时你会发现页面回到的初始状态,滚动条也回到了原点。现在解释下为什么会这样。

    首先页面没有启用缓存,那么每次返回的时候页面总是会刷新,如何刷新的?其实就是返回的时候系统重新调用InitializeComponent方法,原来所有的数据和状态都不复存在,就像一个全新的页面一样(其实你应该把它当成一个全新的页面)。

    如果只能这样的话我们肯定会抓狂,还好微软留了两个方法,LoadState和SaveState,其实这两个方法是项目模版中自定义的方法。

    •  LoadState方法,当该页面第一次加载(注意不是返回)的时候,参数pageState为null。当页面是从前一页返回的时候加载的,那么参数pageState参数包含了你在SaveState方法里保存的数据。这时你就可以恢复页面的数据和状态了。
    •  SaveState方法,当从当前页导航进入其他页的时候,会在当前页调用SaveSate方法,这时候你需要把页面的数据和状态保存到pageState中,供LoadState的时候使用保存的 数据

    我们以GroupedItemsPage为例,首先我们需要在SaveState里面添加一些代码:

     protected override void SaveState(Dictionary<string, object> pageState)
            {
                base.SaveState(pageState);
                //把DefaultViewModel中的所有数据转移到pageState中
                foreach (var item in this.DefaultViewModel)
                {
                    pageState[item.Key] = item.Value;
                }
                //保存滚动条滚动状态,这里只保存HorizontalOffset
                var scroll = itemGridView.GetVisualDescendants<ScrollViewer>().FirstOrDefault();
                pageState["HorizontalOffset"] = scroll == null ? 0 : scroll.HorizontalOffset;
            }

    然后在LoadState里面添加如下代码:

      protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
            {
                //pageState不为null,那么表明是返回状态,这时候需要恢复数据和状态
                if (pageState != null)
                {
                    itemGridView.LayoutUpdated += itemGridView_LayoutUpdated;
                    if (pageState != null)
                    {
                        foreach (var item in pageState)
                        {
                            this.DefaultViewModel[item.Key] = item.Value;
                        }
                    }
                }
                else
                {
                    // pageState为null,表明是第一次加载页面,需要初始化数据
                    var sampleDataGroups = SampleDataSource.GetGroups((String)navigationParameter);
                    this.DefaultViewModel["Groups"] = sampleDataGroups;
                }
    
            }

    后面添加itemGridView_LayoutUpdated事件的方法:

      //页面的滚动条
            ScrollViewer scroll;
            void itemGridView_LayoutUpdated(object sender, object e)
            {
                if (scroll == null)
                    scroll = itemGridView.GetVisualDescendants<ScrollViewer>().FirstOrDefault();
                if (scroll != null)
                {
                    if (this.DefaultViewModel.ContainsKey("HorizontalOffset"))
                    {
                        double d = (double)this.DefaultViewModel["HorizontalOffset"];
                        scroll.ScrollToHorizontalOffset(d);
                        //滚动条的状态恢复不是一下子就恢复的,可能需要多次调用ScrollToHorizontalOffset才能准确恢复
                        if (d == scroll.HorizontalOffset)
                            itemGridView.LayoutUpdated -= itemGridView_LayoutUpdated;
                    }
                    else//如果DefaultViewModel中没有HorizontalOffset数据,那么就不需要恢复滚动条状态了
                        itemGridView.LayoutUpdated -= itemGridView_LayoutUpdated;
                }
            }

    添加完这些代码就可以直接运行了,这时你会发现,不管是页面数据还是页面状态都能准确的恢复到上一次的状态。

    上面代码中涉及到的GetVisualDescendants方法是一个扩展方法,具体代码如下:

     public static class VisualTreeExtensions
        {
            public static IEnumerable<T> GetVisualDescendants<T>(this DependencyObject element) where T : DependencyObject
            {
                return element.GetVisualDescendants().OfType<T>();
            }
    
            /// <summary>
            /// 获取某个元素的所有子孙节点
            /// </summary>
            public static IEnumerable<DependencyObject> GetVisualDescendants(this DependencyObject element)
            {
                if (element == null)
                {
                    throw new ArgumentNullException("element");
                }
    
                return GetVisualDescendantsAndSelfIterator(element).Skip(1);
            }
    
            private static IEnumerable<DependencyObject> GetVisualDescendantsAndSelfIterator(DependencyObject element)
            {
                Queue<DependencyObject> remaining = new Queue<DependencyObject>();
                remaining.Enqueue(element);
    
                while (remaining.Count > 0)
                {
                    DependencyObject obj = remaining.Dequeue();
                    yield return obj;
    
                    foreach (DependencyObject child in obj.GetVisualChildren())
                    {
                        remaining.Enqueue(child);
                    }
                }
            }
    
            public static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject element)
            {
                if (element == null)
                {
                    throw new ArgumentNullException("element");
                }
    
                return GetVisualChildrenAndSelfIterator(element).Skip(1);
            }
    
            private static IEnumerable<DependencyObject> GetVisualChildrenAndSelfIterator(this DependencyObject element)
            {
    
                yield return element;
    
                int count = VisualTreeHelper.GetChildrenCount(element);
                for (int i = 0; i < count; i++)
                {
                    yield return VisualTreeHelper.GetChild(element, i);
                }
            }
    
        }
  • 相关阅读:
    为什么为 const 变量重新赋值不是个静态错误
    带有“非简单参数”的函数为什么不能包含 "use strict" 指令
    arguments 对象的老历史
    去掉你代码里的 document.write("<script...
    SameSite Cookie,防止 CSRF 攻击
    扼杀 304,Cache-Control: immutable
    V8 的 typeof null 返回 "undefined" 的 bug 是怎么回事
    IntersectionObserver API
    passive 的事件监听器
    esnext:最后一个参数后面也允许加逗号了
  • 原文地址:https://www.cnblogs.com/dagehaoshuang/p/2638388.html
Copyright © 2011-2022 走看看