1.导航接口
/// <summary>
/// 自定义导航服务接口
/// </summary>
public interface ICustomNavigationService
{
/// <summary>
/// 记录不同页面容器的当前页面键集合
/// </summary>
List<(string pageHostName, ApplicationPage page)> CurrentPageKeyList
{
get;
}
/// <summary>
/// 回退到上一页
/// </summary>
void GoBack(string pageHostName);
/// <summary>
/// 导航到指定键页面
/// </summary>
/// <param name="pageHostName">容器名称</param>
/// <param name="pageKey">页面ApplicationPage类型键</param>
void NavigateTo(string pageHostName, ApplicationPage pageKey);
/// <summary>
/// 导航到指定键页面并且传递参数
/// </summary>
/// <param name="pageHostName">容器名称</param>
/// <param name="pageKey">页面ApplicationPage类型键</param>
/// <param name="parameter">页面间传递的参数:一般情况下,请直接在IOC容器中获取ViewModel并且传递值</param>
void NavigateTo(string pageHostName, ApplicationPage pageKey, object parameter);
}
2.实现
public class NavigationService : ICustomNavigationService
{
/// <summary>
/// 锁对象
/// </summary>
private object _navLock = new object();
/// <summary>
/// 缓存页面键值对
/// </summary>
private Dictionary<ApplicationPage, BasePage> _navigationDic;
/// <summary>
/// 记录历史页面字典栈
/// </summary>
private Dictionary<string, Stack<ApplicationPage>> _historyStack;
/// <summary>
/// 记录不同页面容器的当前页面键集合
/// </summary>
public List<(string pageHostName, ApplicationPage page)> CurrentPageKeyList { get; set; }
/// <summary>
/// 构造器
/// </summary>
public NavigationService()
{
_navigationDic = new Dictionary<ApplicationPage, BasePage>();
_historyStack=new Dictionary<string, Stack<ApplicationPage>>();
CurrentPageKeyList = new List<(string, ApplicationPage)>();
}
/// <summary>
/// 回退到上一页
/// </summary>
public void GoBack(string pageHostName)
{
throw new NotImplementedException();
}
/// <summary>
/// 导航到指定键页面
/// </summary>
/// <param name="pageHostName">容器名称</param>
/// <param name="pageKey">页面ApplicationPage类型键</param>
public void NavigateTo(string pageHostName, ApplicationPage pageKey)
{
NavigateTo(pageHostName, pageKey, null);
}
/// <summary>
/// 导航到指定键页面并且传递参数
/// </summary>
/// <param name="pageHostName">容器名称</param>
/// <param name="pageKey">页面ApplicationPage类型键</param>
/// <param name="parameter">页面间传递的参数:一般情况下,请直接在IOC容器中获取ViewModel并且传递值</param>
public void NavigateTo(string pageHostName, ApplicationPage pageKey, object parameter)
{
lock (_navLock)
{
//导航页末调用Configure方法配置
if (!_navigationDic.ContainsKey(pageKey))
{
throw new ArgumentException("This key is not configured!");
}
//找到页面容器
List<PageHost> pageHosts = new List<PageHost>();
Application.Current.MainWindow.FindAllChilds<PageHost>(ref pageHosts);
var pageHost = pageHosts.FirstOrDefault(t => t.Name == pageHostName);
if (pageHost == null)
{
throw new Exception("PageHost is not Configured!");
}
//当前页列表未记录当前键,初始化当前键与历史列表
if (!CurrentPageKeyList.Any(t => t.pageHostName == pageHostName))
{
CurrentPageKeyList.Add((pageHostName, pageKey));
_historyStack.Add(pageHostName, new Stack<ApplicationPage>());
}
else
{
//将当前键加入历史列表栈
_historyStack[pageHostName].Push(CurrentPageKeyList.First(t => t.pageHostName == pageHostName).page);
CurrentPageKeyList.Remove(CurrentPageKeyList.First(t => t.pageHostName == pageHostName));
CurrentPageKeyList.Add((pageHostName, pageKey));
}
//获取页面框架
var newPageFrame = pageHost.newPage;
var oldPageFrame = pageHost.oldPage;
//获取xaml传入的当前viewmodel
var currentPageViewModel = pageHost.CurrentPageViewModel;
//在页未改变情况下只更新ViewModel
if (newPageFrame.Content is BasePage page && page == _navigationDic[pageKey])
page.ViewModelObject= currentPageViewModel;
var oldPageContent = newPageFrame.Content;
newPageFrame.Content = null;
oldPageFrame.Content = oldPageContent;
//动画效果
if (oldPageContent is BasePage oldPage)
{
oldPage.ShouldAnimateOut = true;
Task.Delay((int)(oldPage.SlideSeconds * 1000)).ContinueWith((t) =>
{
Application.Current.Dispatcher.Invoke(() => oldPageFrame.Content = null);
});
}
//切换页面
BasePage showPage = _navigationDic[pageKey];
showPage.ViewModelObject = currentPageViewModel;
showPage.ExtraData = parameter;
newPageFrame.Content = showPage;
}
}
/// <summary>
/// 配置页面键值对
/// </summary>
/// <param name="pageKey">页面ApplicationPage类型键</param>
/// <param name="basePage">页面</param>
/// <exception cref="ArgumentException">键或者值被引用过抛出异常</exception>
public void Configure(ApplicationPage pageKey,BasePage basePage)
{
lock (_navLock)
{
if (_navigationDic.ContainsKey(pageKey))
{
throw new ArgumentException("This key is already used:"+pageKey);
}
if (_navigationDic.Values.Any(t => t == basePage))
{
throw new ArgumentException("This basepage is already configured with key "+ _navigationDic.First(t => t.Value == basePage).Key);
}
_navigationDic.Add(pageKey,basePage);
}
}
}
3.使用mvvmlight自定义viewmodel
public class BaseViewModel:ViewModelBase
{
protected object mPropertyValueCheckLock=new object();
protected async Task RunCommandAsync(Expression<Func<bool>> flagUpdating,Func<Task> action)
{
lock (mPropertyValueCheckLock)
{
if (flagUpdating.GetPropertyValue())
{
return;
}
flagUpdating.SetPropertyValue(true);
}
try
{
await action();
}
finally
{
flagUpdating.SetPropertyValue(false);
}
}
protected async Task<T> RunCommandAsync<T>(Expression<Func<bool>> flagUpdating, Func<Task<T>> action,T defaultValue)
{
lock (mPropertyValueCheckLock)
{
if (flagUpdating.GetPropertyValue())
{
return defaultValue;
}
flagUpdating.SetPropertyValue(true);
}
try
{
return await action();
}
finally
{
flagUpdating.SetPropertyValue(false);
}
}
}
public static class DependencyObjectHelpers
{
public static void FindAllChilds(this DependencyObject d, ref List<DependencyObject> dependencyObjects)
{
var count = VisualTreeHelper.GetChildrenCount(d);
for (int i = 0; i < count; i++)
{
DependencyObject dependencyObject = VisualTreeHelper.GetChild(d, i);
dependencyObjects.Add(dependencyObject);
FindAllChilds(dependencyObject, ref dependencyObjects);
}
}
public static void FindAllChilds<T>(this DependencyObject d, ref List<T> dependencyObjects) where T:class
{
var count = VisualTreeHelper.GetChildrenCount(d);
for (int i = 0; i < count; i++)
{
DependencyObject dependencyObject = VisualTreeHelper.GetChild(d, i);
if (dependencyObject.GetType() == typeof(T))
{
dependencyObjects.Add(dependencyObject as T);
}
FindAllChilds(dependencyObject, ref dependencyObjects);
}
}
}
public enum ApplicationPage
{
None=0
}
`public partial class PageHost : UserControl
{
public ApplicationPage CurrentPage
{
get { return (ApplicationPage)GetValue(CurrentPageProperty); }
set { SetValue(CurrentPageProperty, value); }
}
public static readonly DependencyProperty CurrentPageProperty =
DependencyProperty.Register("CurrentPage", typeof(ApplicationPage), typeof(PageHost), new PropertyMetadata(default(ApplicationPage),null, CurrentPagePropertyChanged));
public BaseViewModel CurrentPageViewModel
{
get { return (BaseViewModel)GetValue(CurrentPageViewModelProperty); }
set { SetValue(CurrentPageViewModelProperty, value); }
}
public static readonly DependencyProperty CurrentPageViewModelProperty =
DependencyProperty.Register("CurrentPageViewModel", typeof(BaseViewModel), typeof(PageHost), new PropertyMetadata(0));
public PageHost()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(this))
newPage.Content = null;
}
private static object CurrentPagePropertyChanged(DependencyObject d, object value)
{
//ioc navigation
return value;
}
}
```csharp
public class BasePage : UserControl
{
#region Private Member
/// <summary>
/// The View Model associated with this page
/// </summary>
private object mViewModel;
#endregion
#region Public Properties
/// <summary>
/// The animation the play when the page is first loaded
/// </summary>
public PageAnimation PageLoadAnimation { get; set; } = PageAnimation.SlideAndFadeInFromRight;
/// <summary>
/// The animation the play when the page is unloaded
/// </summary>
public PageAnimation PageUnloadAnimation { get; set; } = PageAnimation.SlideAndFadeOutToLeft;
/// <summary>
/// The time any slide animation takes to complete
/// </summary>
public float SlideSeconds { get; set; } = 0.4f;
/// <summary>
/// A flag to indicate if this page should animate out on load.
/// Useful for when we are moving the page to another frame
/// </summary>
public bool ShouldAnimateOut { get; set; }
/// <summary>
/// The View Model associated with this page
/// </summary>
public object ViewModelObject
{
get => mViewModel;
set
{
// If nothing has changed, return
if (mViewModel == value)
return;
// Update the value
mViewModel = value;
// Fire the view model changed method
OnViewModelChanged();
// Set the data context for this page
DataContext = mViewModel;
}
}
public object ExtraData { get; set; }
#endregion
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public BasePage()
{
// Don't bother animating in design time
if (DesignerProperties.GetIsInDesignMode(this))
return;
// If we are animating in, hide to begin with
if (PageLoadAnimation != PageAnimation.None)
Visibility = Visibility.Collapsed;
// Listen out for the page loading
Loaded += BasePage_LoadedAsync;
}
#endregion
#region Animation Load / Unload
/// <summary>
/// Once the page is loaded, perform any required animation
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void BasePage_LoadedAsync(object sender, System.Windows.RoutedEventArgs e)
{
// If we are setup to animate out on load
if (ShouldAnimateOut)
// Animate out the page
await AnimateOutAsync();
// Otherwise...
else
// Animate the page in
await AnimateInAsync();
}
/// <summary>
/// Animates the page in
/// </summary>
/// <returns></returns>
public async Task AnimateInAsync()
{
// Make sure we have something to do
if (PageLoadAnimation == PageAnimation.None)
return;
switch (PageLoadAnimation)
{
case PageAnimation.SlideAndFadeInFromRight:
// Start the animation
await this.SlideAndFadeInAsync(AnimationSlideInDirection.Right, false, SlideSeconds, size: (int)Application.Current.MainWindow.Width);
break;
}
}
/// <summary>
/// Animates the page out
/// </summary>
/// <returns></returns>
public async Task AnimateOutAsync()
{
// Make sure we have something to do
if (PageUnloadAnimation == PageAnimation.None)
return;
switch (PageUnloadAnimation)
{
case PageAnimation.SlideAndFadeOutToLeft:
// Start the animation
await this.SlideAndFadeOutAsync(AnimationSlideInDirection.Left, SlideSeconds);
break;
}
}
#endregion
/// <summary>
/// Fired when the view model changes
/// </summary>
protected virtual void OnViewModelChanged()
{
}
}
/// <summary>
/// A base page with added ViewModel support
/// </summary>
public class BasePage<VM> : BasePage
where VM : BaseViewModel, new()
{
#region Public Properties
/// <summary>
/// The view model associated with this page
/// </summary>
public VM ViewModel
{
get => (VM)ViewModelObject;
set => ViewModelObject = value;
}
#endregion
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public BasePage() : base()
{
// If in design time mode...
if (DesignerProperties.GetIsInDesignMode(this))
// Just use a new instance of the VM
ViewModel = new VM();
else
// Create a default view model
ViewModel = SimpleIoc.Default.GetInstance<VM>() ?? new VM();
}
/// <summary>
/// Constructor with specific view model
/// </summary>
/// <param name="specificViewModel">The specific view model to use, if any</param>
public BasePage(VM specificViewModel = null) : base()
{
// Set specific view model
if (specificViewModel != null)
ViewModel = specificViewModel;
else
{
// If in design time mode...
if (DesignerProperties.GetIsInDesignMode(this))
// Just use a new instance of the VM
ViewModel = new VM();
else
{
// Create a default view model
ViewModel = SimpleIoc.Default.GetInstance<VM>() ?? new VM();
}
}
}
#endregion
}