Windows 10(UWP)開發技巧 - PageUserControl

       【本系列須要具備必定開發基礎】git

       咱們在開發中常常遇到這樣的場景:github

       1.呈現詳細信息,且包含一些操做。如:查看原圖,支持放大,縮小,多圖。緩存

       2.執行特定的行爲,且要有回執結果。如:選擇聯繫人,選中某圖,用戶登陸。ide

       廣泛的解決方案就是封裝一個UserControl放到頁面裏,控制其顯隱性。若是功能不多,那無所謂,可稍微複雜一點的,封裝成單獨的一個頁面不是更好嗎?還能節省當前頁面的資源。此方法可以解決場景1,可是場景2須要回執結果,又該怎麼辦,總不能用全局變量吧。PageUserControl就是主要解決這些問題而封裝,它將包含特定邏輯的頁面封裝成僞控件,使其能夠單獨調用,且能夠反饋執行結果。ui

       調用方法如圖:this

      

      PageUserControlspa

      PageUserControl是一個抽象的泛型類,做爲封裝控件的父類。原理:監聽Frame的Navigated事件,利用緩存的兩個頁面變量,區別出是Forward仍是Back,而後分別作傳值和取值操做。廢話很少說,直接上代碼:code

    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,這裏就不實現了。blog

 
       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

 轉載請註明出處。

相關文章
相關標籤/搜索