• 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

     转载请注明出处。

  • 相关阅读:
    selenium2基本控件介绍及其代码
    selenium2元素定位Xpath和cssSelector
    Selenium2启动浏览器且加载插件
    bash之条件测试if/else
    bash脚本编写基础
    Android后台的linux一直保持唤醒状态,不进入睡眠
    Linux任务计划、周期性任务执行
    C#进阶系列——WebApi 跨域问题解决方案:CORS(转载)
    linq group by
    echart 分组属性
  • 原文地址:https://www.cnblogs.com/roler/p/4816473.html
Copyright © 2020-2023  润新知