以前做UWP开发都是使用MvvmLight,主要是简单易上手,同时也写了很多MvvmLight的开发系列文章:
UWP开发之Mvvmlight实践九:基于MVVM的项目架构分享
UWP开发之Mvvmlight实践八:为什么事件注销处理要写在OnNavigatingFrom中
UWP开发之Mvvmlight实践七:如何查找设备(Mobile模拟器、实体手机、PC)中应用的Log等文件
UWP开发之Mvvmlight实践六:MissingMetadataException解决办法(.Net Native下Default.rd.xml配置问题)
UWP开发之Mvvmlight实践五:SuspensionManager中断挂起以及复原处理
UWP开发之Mvvmlight实践四:{x:bind}和{Binding}区别详解
UWP开发之Mvvmlight实践三:简单MVVM实例开发(图文详解付代码)
UWP开发之Mvvmlight实践二:Mvvmlight的核心框架MVVM与MVC、MVP的区别(图文详解)
UWP开发之Mvvmlight实践一:如何在项目中添加使用Mvvmlight(图文详解)
出于开发效率考虑,以后打算都使用Prism或者Template10开发,其中很多的实现原理与MvvmLight相似。此次基于Prism.Windows完成的UWP应用虽然功能不是那么复杂,但是基本上用全了Prism的优良特性,特写下次篇做备忘。
总结一:IOC控制反转容器
目前只支持Unity,SimpleInjector,Autofac三个,相比MvvmCross或者Mvvmlight框架则选择更加灵活。
使用方法(例如:Unity):
1,App类继承于IOC对应的Application类。
sealed partial class App : PrismUnityApplication {} |
2,依赖注入。(最好所有的注入都写在这个方法里面,后续的启动处理以及页面转移等可以立马使用。)
protected override Task OnInitializeAsync(IActivatedEventArgs args)
{
Container.RegisterInstance<INavigationService>(NavigationService);
Container.RegisterInstance<ISessionStateService>(SessionStateService);
Container.RegisterType<ISettingService, SettingService>(new ContainerControlledLifetimeManager());
return base.OnInitializeAsync(args);
}
3,调用。
- App.cs内调用:
await Container.Resolve<IAppStartupService>().CreateDataBaseAsync();
- ViewModel内使用:
private INavigationService _navigationService; public HomePageViewModel(INavigationService navigationService) { _navigationService = navigationService; }
总结二:自动绑定
自动绑定的命名规则:
View名:HomePage
ViewModel名:HomePageViewModel
其次Views与ViewModels文件夹最好在同一个程序集下,然后View的头部添加如下两行代码,就可以自动绑定ViewModel。
1 2 3 4 5 6 7 8 9 10 | <? xml version="1.0" encoding="utf-8" ?> xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" prism:ViewModelLocator.AutowireViewModel="True" Title="MainPage"> < StackLayout HorizontalOptions="Center" VerticalOptions="Center"> < Label Text="{Binding Title}" /> </ StackLayout > </ ContentPage > |
如果View与ViewModel分层了,通过自定义ViewModelTypeResolver也可以实现自动绑定。
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(
viewType => {
// 自由设置
return Type.GetType("");
});
Prism.Mvvm下ViewModelLocationProvide.cs的默认设置如下:
static Func<Type, Type> _defaultViewTypeToViewModelTypeResolver =
viewType =>
{
var viewName = viewType.FullName;
viewName = viewName.Replace(".Views.", ".ViewModels.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var suffix = viewName.EndsWith("View") ? "Model" : "ViewModel";
var viewModelName = String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}", viewName, suffix, viewAssemblyName);
return Type.GetType(viewModelName);
};
总结三:NavigationService
全部接口:
- bool Navigate(string pageToken, object parameter);
- void GoBack();
- bool CanGoBack();
- void GoForward();
- bool CanGoForward();
- void ClearHistory();
- void RemoveFirstPage(string pageToken = null, object parameter = null);
- void RemoveLastPage(string pageToken = null, object parameter = null);
- void RemoveAllPages(string pageToken = null, object parameter = null);
- void RestoreSavedNavigation();
- void Suspending();
有了它再也不用担心一些特殊的页面漂移问题。
1,App.cs内部使用。(类以及包含NavigationService成员属性):
1 | // 启动页面表示 |
NavigationService.Navigate("Home", null);
2,ViewModel使用。
1 2 3 4 5 6 | private INavigationService _navigationService; public HomePageViewModel(INavigationService navigationService) { _navigationService = navigationService; } |
1 | |
1 | _navigationService.ClearHistory()等 |
1 | |
注意:
由于10,11 的记录保存与复原都是使用DataContractSerializer序列化,所以页面漂移参数最好使用字符串或者数字。
总结四:中断还原处理
SessionStateService是类似SuspensionManager专门处理中断。
1,App.cs内部设置
依赖注入
protected override Task OnInitializeAsync(IActivatedEventArgs args)
{
Container.RegisterInstance<ISessionStateService>(SessionStateService);
return base.OnInitializeAsync(args);
}
类型注册
protected override void OnRegisterKnownTypesForSerialization() { SessionStateService.RegisterKnownType(typeof(FavoriteInfo)); SessionStateService.RegisterKnownType(typeof(ServerInfo)); // 这个比较重要 SessionStateService.RegisterKnownType(typeof(ObservableCollection<FavoriteInfo>)); }
2,ViewPage类型设置
<prismMvvm:SessionStateAwarePage
x:Class="PrismUnityApp1.Views.SettingPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:prismMvvm="using:Prism.Windows.Mvvm" prismMvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
3,ViewModel中数据成员属性设置
private ObservableCollection<FavoriteInfo> _favoriteFolders; [RestorableState] public ObservableCollection<FavoriteInfo> FavoriteFolders {
get { return _favoriteFolders; }
set { SetProperty(ref _favoriteFolders, value); } }
4,.net native相关的Default.rd.xml设置
<Type Name="PrismUnityApp1.DataModels.FavoriteInfo" DataContractSerializer="Required Public" />
<Namespace Name="System.Collections.ObjectModel" >
<TypeInstantiation Name="ObservableCollection" Arguments="PrismUnityApp1.DataModels.FavoriteInfo" DataContractSerializer="Required Public" />
</Namespace>
总结五:ResourceLoader
1,依赖注入。
protected override Task OnInitializeAsync(IActivatedEventArgs args)
{
Container.RegisterInstance<IResourceLoader>(new ResourceLoaderAdapter(new ResourceLoader()));
return base.OnInitializeAsync(args);
}
2,调用GetString()获取数据。
1 | public MenuViewModel(INavigationService navigationService, |
IResourceLoader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | resourceLoader) { _navigationService = navigationService; Commands = new ObservableCollection<MenuItemViewModel> { new MenuItemViewModel { DisplayName = resourceLoader.GetString( "MainPageMenuItemDisplayName" ), FontIcon = "\ue15f" , Command = new DelegateCommand(NavigateToMainPage, CanNavigateToMainPage) }, new MenuItemViewModel { DisplayName = resourceLoader.GetString( "SecondPageMenuItemDisplayName" ), FontIcon = "\ue19f" , Command = new DelegateCommand(NavigateToSecondPage, CanNavigateToSecondPage) } }; } |
总结六:属性验证Validatable
类似Asp.net MVC支持以下Validation:
- Required
- CreditCard
- EmailAddress
- PhoneNumber
- Range
- MinLength
- MaxLenght
- RegularExpression
- Enumeration
- URL
使用方法:1,继承与ValidatableBindableBase
1 | public class Address : |
ValidatableBindableBase
1 | |
2,成员设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 | [Required(ErrorMessage = "Name is required." )] public string Name { get { return name; } set { SetProperty( ref name, value); } } [RegularExpression( @"[ABCD]\d{2,5}" , ErrorMessage= "Location is Quadrant (A -> D) and Sector (2 -> 5 digits)" )] public string Location { get { return location; } set { SetProperty( ref location, value); } } |
1 | |
3,控件绑定
1 | <TextBox Text= "{Binding Name, Mode=TwoWay}" Header= "Name" /> |
1 | <TextBlock Text= "{Binding Errors[Name][0]}" Foreground= "Red" HorizontalAlignment= "Right" /> |
1 | |
1 | <ItemsControl x:Name= "ErrorList" ItemsSource= "{Binding Errors.Errors}" /> |
1 | |
备注:通过继承ValidationAttribute 可以自定义各种验证逻辑。
1 | |
总结七:菜单设置时机
App.cs内部的CreateShell方法中设置:
1 2 3 4 5 6 | protected override UIElement CreateShell(Frame rootFrame) { var menuPage = Container.Resolve<MenuPage>(); menuPage.NavigationFrame.Content = rootFrame; return menuPage; } |
1 | |
总结八:数据库创建时机
以前我们一般都是在OnLaunched方法内部添加如下代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | protected override void OnLaunched(LaunchActivatedEventArgs e) { Frame rootFrame = Window.Current.Content as Frame; // 不要在窗口已包含内容时重复应用程序初始化, // 只需确保窗口处于活动状态 if (rootFrame == null ) { // 创建要充当导航上下文的框架,并导航到第一页 rootFrame = new Frame(); rootFrame.NavigationFailed += OnNavigationFailed; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { //TODO: 从之前挂起的应用程序加载状态 } // 将框架放在当前窗口中 Window.Current.Content = rootFrame; } if (e.PrelaunchActivated == false ) { if (rootFrame.Content == null ) { // 数据库创建 createDBAsync(); // 当导航堆栈尚未还原时,导航到第一页, // 并通过将所需信息作为导航参数传入来配置 // 参数 rootFrame.Navigate( typeof (MainPage), e.Arguments); } // 确保当前窗口处于活动状态 Window.Current.Activate(); } } Task createDBAsync() { // 略 return Task.FromResult( true ); } |
由于Prism封装了Application类,所以代码添加的位置也变了:
1 2 3 4 5 | protected override Task OnInitializeAsync(IActivatedEventArgs args) { createDBAsync(); return base .OnInitializeAsync(args); } |
基本就这些东西,总结一下方便以后开发,同时也希望对园友们有帮助。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下