Windows 10(UWP)开发技巧

       【本系列需要具有一定开发基础】

       我们在开发中经常遇到这样的场景:

       1.呈现详细信息,且包含一些操作。如:查看原图,支持放大,缩小,多图。

       2.执行特定的行为,且要有回执结果。如:选择联系人,选中某图,用户登录。

       普遍的解决方案就是封装一个UserControl放到页面里,控制其显隐性。如果功能很少,那无所谓,可稍微复杂一点的,封装成单独的一个页面不是更好吗?还能节省当前页面的资源。此方法能够解决场景1,但是场景2需要回执结果,又该怎么办,总不能用全局变量吧。PageUserControl就是主要解决这些问题而封装,它将包含特定逻辑的页面封装成伪控件,使其可以单独调用,且可以反馈执行结果。

       调用方法如图:

      

      PageUserControl

      PageUserControl是一个抽象的泛型类,作为封装控件的父类。原理:监听Frame的Navigated事件,利用缓存的两个页面变量,区别出是Forward还是Back,然后分别做传值和取值操作。废话不多说,直接上代码:

    public abstract class PageUserControl<TPage>
        where TPage : Page
    {
        private const string _FrameNameInFramePage = "childrenFrame";

        private Frame _frame;
        private object _frameContentWhenOpened;
        private TPage _page;

        /// <summary>
        /// 获取是否优先呈现在ChildrenFrame中。
        /// </summary>
        public bool IsChildrenFrameFirst { get; protected set; }

        #region Methods

        protected void ShowPage()
        {
            this.OpenPickerPage();
        }

        protected void ShowPage(object parameter)
        {
            this.OpenPickerPage(parameter);
        }

        //若需向调用者返回某值,则需要实现此方法。
        protected virtual void CommitValue(TPage page)
        {
        }

        private void OpenPickerPage(object parameter = null)
        {
            if (null == _frame)
            {
                _frame = Window.Current.Content as Frame;
                if (null != _frame)
                {
                    //这里是约定MainPage页中childrenFrame是子Frame。
                    //此方法并非绝对,仍有很多灵活的方法可以扩展,比如附加属性来指定谁是ChildrenFrame。
                    if (this.IsChildrenFrameFirst && this._frame.CurrentSourcePageType.Equals(typeof(Pages.MainPage)))
                    {
                        var framePage = (Pages.MainPage)_frame.Content;
                        var frameInFramePage = framePage.FindName(_FrameNameInFramePage) as Frame;
                        if (frameInFramePage != null)
                        {
                            this._frame = frameInFramePage;
                        }
                    }

                    _frameContentWhenOpened = _frame.Content;

                    _frame.Navigated += OnFrameNavigated;
                    _frame.NavigationStopped += OnFrameNavigationStopped;
                    _frame.NavigationFailed += OnFrameNavigationFailed;

                    if (parameter == null)
                    {
                        _frame.Navigate(typeof(TPage));
                    }
                    else
                    {
                        _frame.Navigate(typeof(TPage), parameter);
                    }
                }
            }
        }

        private void ClosePickerPage()
        {
            // 注销事件
            if (null != _frame)
            {
                _frame.Navigated -= OnFrameNavigated;
                _frame.NavigationStopped -= OnFrameNavigationStopped;
                _frame.NavigationFailed -= OnFrameNavigationFailed;

                _frame = null;
                _frameContentWhenOpened = null;
            }

            //若缓存页面有值,则尝试做提交处理。
            if (null != this._page)
            {
                this.CommitValue(this._page);
                this._page = null;
            }
        }

        #endregion

        #region Events

        private void OnFrameNavigated(object sender, NavigationEventArgs e)
        {
            //若是Back则做关闭处理,若是Forward则把新页缓存。
            if (e.Content == _frameContentWhenOpened)
            {
                ClosePickerPage();
            }
            else if (null == this._page)
            {
                var page = e.Content as TPage;

                if (page != null)
                {
                    this._page = page;
                }
            }
        }

        private void OnFrameNavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            ClosePickerPage();
        }

        private void OnFrameNavigationStopped(object sender, NavigationEventArgs e)
        {
            ClosePickerPage();
        }

        #endregion
    }

        以上的代码对Frame做了简单扩展,使其能支持在子Frame中呈现(主要是考虑到UWP的SpiltView),但是采用的固定约束,并不灵活。各位看官可以自行扩展,比如:使用附加属性来标识某一个Frame,这里就不实现了。

 
       PageUserControl泛型类的使用参考如下:
public class ImageChooser : PageUserControl<ImageChooserPage>
    {
        public ImageChooser()
        {
            //优先在ChildrenFrame呈现。
            base.IsChildrenFrameFirst = true;
        }

        public void Show()
        {
            base.ShowPage();
        }

        protected override void CommitValue(ImageChooserPage page)
        {
            base.CommitValue(page);

            //若标识结果的页面属性值有效,则通过事件抛给调用者。
            if (!string.IsNullOrWhiteSpace(page.Value))
            {
                this.OnCompleted(page.Value);
            }
        }

        public event EventHandler<ChooseImageCompletedEventArgs> Completed;
        private void OnCompleted(string image)
        {
            var handler = this.Completed;
            if (handler != null)
            {
                handler(this, new ChooseImageCompletedEventArgs(image));
            }
        }
    }

    public class ChooseImageCompletedEventArgs : EventArgs
    {
        public string Image { get; private set; }

        internal ChooseImageCompletedEventArgs(string image)
        {
            this.Image = image;
        }
    }

      以上代码是针对需要返回值的场景,如果无须返回值则留空或者不重写CommitValue方法即可。

      注意:调用页和控件页需要对NavigationCacheMode操作如下图,使其保证PageUserControl的页面变量唯一性,具体原因参考MSDN-NavigationCacheMode属性介绍。
        public HomePage()
        {
            this.InitializeComponent();
            this.NavigationCacheMode = NavigationCacheMode.Required;
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);
            if (e.NavigationMode == NavigationMode.Back)
            {
                this.NavigationCacheMode = NavigationCacheMode.Disabled;
            }
        }

        如何正确应用在MVVM模式中?使用Behavior!

        参考示例代码ListPicker。在本示例代码中封装了一个名为ListPicker的PageUserControl,它接受ItemsSources,ItemTemplate,SelectedItem参数,分别对应ListPickerPage中ListView的相同属性。ShowListPickerAction封装了对ListPicker的调用。
        <Button Content="图片"
                Grid.Row="1">
            <i:Interaction.Behaviors>
                <core:EventTriggerBehavior EventName="Click">
                    <behaviors:ShowListPickerAction ItemsSource="{Binding Images}" ItemTemplate="{ThemeResource ImageItemTemplate}" ItemsPickedCommand="{Binding ImagePickedCommand}" ItemsPickedInputConverter="{StaticResource ListPickerItemsPickedEventArgsConverter}"/>
                </core:EventTriggerBehavior>
            </i:Interaction.Behaviors>
        </Button>
        详细实现过程,请参考示例:
        点击打开链接
        https://github.com/rolerzhang/UWP-DevSkills

 转载请注明出处。

原文地址:https://www.cnblogs.com/roler/p/4816473.html