你的位置:首页 > ASP.net教程

[ASP.net教程]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

 转载请注明出处。