前言:接入后台数据,前后台实体类都基本一直,稍微各有各的扩展,后台可以采用modelfirst,前言还是采用dbfrist好了,直接生成实体类。
然后在程序包控制台输入: Scaffold-DbContext "Server=.;Database=Colder.Admin.AntdVue;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -UseDatabaseNames 生成成功(是不是很省事):
public class Operator : IOperator { public string UserId { get { return Property?.Id; } } /// <summary> /// 当前操作者UserName /// </summary> public string UserName { get; set; } public string Avatar { get; set; } public Base_User Property { get; set; } public List<string> Permissions { get; set; } //菜单树 public ObservableCollection<AMenuItem> MenuItems { get; set; } //打平用于查询的菜单 public ObservableCollection<AMenuItem> SearchMenus { get; set; } }
public interface IOperator { /// <summary> /// 当前操作者UserId /// </summary> string UserId { get; } string UserName { get; set; } string Avatar { get; set; } /// <summary> /// 当前操作者 /// </summary> Base_User Property { get; set; } List<string> Permissions { get; set; } //菜单树 ObservableCollection<AMenuItem> MenuItems { get; set; } //打平用于查询的菜单 ObservableCollection<AMenuItem> SearchMenus { get; set; } #region 操作方法 #endregion }
别忘了在App.xaml.cs注册
protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.Register<IDataProvider, ApiDataProvider>(); containerRegistry.RegisterSingleton<IOperator, Operator>(); }
private async void InitMenu() { var userinfo = await _dataProvider.GetData<UserInfoPermissions>("/Base_Manage/Home/GetOperatorInfo"); if (!userinfo.Success) { throw new System.Exception(userinfo.Msg); } _operator.Property = userinfo.Data.UserInfo; _operator.Permissions = userinfo.Data.Permissions; _operator.Avatar = userinfo.Data.UserInfo.Avatar; var menuinfo = await _dataProvider.GetData<List<Base_ActionTree>>("/Base_Manage/Home/GetOperatorMenuList"); if (!menuinfo.Success) { throw new System.Exception(menuinfo.Msg); } BuildMenu(menuinfo.Data); } private void BuildMenu(List<Base_ActionTree> base_Actions) { var nodes = base_Actions.Where(p => string.IsNullOrEmpty(p.ParentId)); foreach (var node in nodes) { AMenuItem aMenuItem = new AMenuItem() { Glyph = node.Icon, Label = node.Text, Code = node.Url, Type = node.Type, ParentId = node.ParentId, Id = node.Id }; MenuItems.Add(aMenuItem); SubBuildMenu(aMenuItem, node, aMenuItem.Id); } } private void SubBuildMenu(AMenuItem menuItem, Base_ActionTree parent, string parentid) { if (parent.Children != null) { foreach (var node in parent.Children) { AMenuItem aMenuItem = new AMenuItem() { Glyph = node.Icon, Label = node.Text, Code = node.Url, Type = node.Type, ParentId = node.ParentId, Id = node.Id }; menuItem.AddChildren(aMenuItem); SubBuildMenu(aMenuItem, node, aMenuItem.Id); } } }
运行,报错。因为需要登录后才能获取菜单,而这个在MainWindow初始化的时候就会执行。但是MainView尚未Loaded,所以我们可以在登录后Loaded的过程中实现获取菜单信息。简单的方法是在MainView.cs里面加Loaded事件,然后传替给MainViewModel进行InitMenu.
核心代码,在实例化View和ViewModel的时候,将View的Loaded触发ViewModel的Loaded。
public static IViewModelResolver UseDefaultConfigure(this IViewModelResolver @this) => @this .IfInheritsFrom<ViewModelBase>((view, viewModel) => { viewModel.Dispatcher = view.Dispatcher; }) .IfInheritsFrom<IViewLoadedAndUnloadedAware>((view, viewModel) => { view.Loaded += (sender, e) => viewModel.OnLoaded(); view.Unloaded += (sender, e) => viewModel.OnUnloaded(); }) .IfInheritsFrom(typeof(IViewLoadedAndUnloadedAware<>), (view, viewModel, interfaceInstance) => { var viewType = view.GetType(); if (interfaceInstance.GenericArguments.Single() != viewType) { throw new InvalidOperationException(); } var onLoadedMethod = interfaceInstance.GetMethod<Action<object>>("OnLoaded", viewType); var onUnloadedMethod = interfaceInstance.GetMethod<Action<object>>("OnUnloaded", viewType); view.Loaded += (sender, args) => onLoadedMethod(sender); view.Unloaded += (sender, args) => onUnloadedMethod(sender); });
在App.xaml.cs中将默认的ConfigureViewModelLocator替换了。
protected override void ConfigureViewModelLocator() { //base.ConfigureViewModelLocator(); ViewModelLocationProvider.SetDefaultViewModelFactory(new ViewModelResolver(() => Container) .UseDefaultConfigure() .ResolveViewModelForView); ViewModelLocationProvider.Register<MainWindow, MainWindowViewModel>(); }
class MainViewModel: BindableBase, IViewLoadedAndUnloadedAware { IContainerExtension _container; IRegionManager _regionManager; IOperator _operator; IDataProvider _dataProvider; public MainViewModel(IContainerExtension container, IRegionManager regionManager, IOperator ioperator, IDataProvider dataProvider) { _container = container; _regionManager = regionManager; _operator = ioperator; _dataProvider = dataProvider; } public void OnLoaded() { InitMenu(); } public void OnUnloaded() { }
万事大吉,运行。报错没有Url,原来是IDataProvider注册的方法有问题,需要用单例来实现,不然登录时候获取的Url就没有保存起来。
containerRegistry.RegisterSingleton<IDataProvider, ApiDataProvider>();
好了,这次真运行起来了。
<?xml version="1.0" encoding="utf-8"?> <configuration> <appSettings> <add key="ServerIP" value="http://121.36.12.76:5000"/> </appSettings> </configuration>
public class LocalSetting { public static string ServerIP { get; set; } = ConfigurationManager.AppSettings["ServerIP"]; }
用的地方:
private string _serverIP = LocalSetting.ServerIP; public string ServerIP { get { return _serverIP; } set { SetProperty(ref _serverIP, value); } }
1.AMenuItem添加字段WpfCode
public string WpfCode { get { if (Code == null) return null; var subcode = Code.Replace("/Index", "IndexView").Replace("/TreeList", "TreeView").Replace("/List", "View").Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries); if (subcode.Length == 1) return Code; subcode[subcode.Length - 1] = $"Views.{subcode[subcode.Length - 1]}"; if (!subcode[subcode.Length - 1].EndsWith("View")) { subcode[subcode.Length - 1] = subcode[subcode.Length - 1] + "View"; } return $"AIStudio.Wpf.{string.Join(".", subcode)}"; } }
将IntroduceView的注册改成FullName
public class MainWindowModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionManager = containerProvider.Resolve<IRegionManager>(); //regionManager.RegisterViewWithRegion("MainContentRegion", typeof(LoginView)); regionManager.RegisterViewWithRegion("MainContentRegion", typeof(IntroduceView).FullName); } public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation(typeof(IntroduceView), typeof(IntroduceView).FullName); } }
另外需要改下页面的传参,使用WpfCode替换Code
<!--顶部菜单--> <Style x:Key="MenuItemStyle" TargetType="{x:Type MenuItem}" BasedOn="{StaticResource MahApps.Styles.MenuItem}"> <Setter Property="MenuItem.Command" Value="{Binding Command}"/> <Setter Property="MenuItem.CommandParameter" Value="{Binding WpfCode}"/> </Style>
_regionManager.RequestNavigate(RegionName, item.WpfCode, NavigationComplete); private void NavigationComplete(NavigationResult result) { if (result.Result == false) { WindowBase.ShowMessageQueue($"{result.Context.Uri.ToString()}打开失败", Identifier); } }
好了,结束。 下一章:实现权限管理的菜单吧。
源码地址:https://gitee.com/akwkevin/aistudio.-wpf.-client.-stepby-step
每一章都有自己的Tag,按照链接进去直接下载就是本章内容。
另外推荐一下我的Wpf客户端框架:https://gitee.com/akwkevin/aistudio.-wpf.-aclient