找回密码
 立即注册
首页 业界区 业界 C# 从零开始使用Layui.Wpf库开发WPF客户端

C# 从零开始使用Layui.Wpf库开发WPF客户端

怃膝镁 5 天前
 一、简介

  最近需要开发一个桌面版的工具软件,之前用得更多的是Winform,作为一个全干工程师,我们也要兼顾下WPF,趁此机会再研究下开源控件库。
  MaQaQ:Winform真好用(有个HZHControls控件库,值得一看)。
二、准备工作

  找了下开源控件库,诸如MaterialDesignInXAML、HandyControl、AduSkin、Adonis-UI、Panuon.WPF.UI、DMSkin等等,以上这些我们都暂时不看。
  本次选用的控件库是Layui.WPF: GitHub - Layui-WPF-Team/Layui-WPF: 这是一个WPF版的Layui前端UI样式库。
  选用的原因是我的渣渣网络在打开其他库的时候都加载很慢,只有它脱颖而出,这就是缘分啊。
  MaQaQ:实际上MaterialDesignInXAML跟HandyControl我之前在别的项目有用过,这两个star数都挺高的,用起来也不错,HandyControl在Gitee上也有库,有兴趣的朋友可以去看看,这次我只是想试试新东西。  
  顺利访问到了GitHub库,我打开了里面的学习文档,他只给了我一个Hello world,真是干!得好。再往下翻,找到了使用说明:
1.png

   使用说明很简洁,看起来很轻松就能用上了,但我不信。那我们就去把源码下载下来吧,感恩开发者,他还给了示例(搬砖党狂喜啊)。打开程序源码,根据提示安装了.net5跟.net7后成功加载,找到LayuiApp,这是示例项目:
2.png

  运行后界面如下(PS:右上角的公告写得真好,值得一读!!!):
3.png

三、开发

  作为一个wpf菜鸡+资深搬砖党,此时不忙搬运,我们先打开LayuiApp的MainWindow.xaml,仔细研究一番:
4.png

  可以看到,原项目采用的是prism框架(一个用于构建复杂但组织良好的 WPF 应用程序的框架,实际上我并不认识它,这是通义千问告诉我的),总之,prism:ViewModelLocator.AutoWireViewModel="True"这一句,就是用于自动关联视图与视图模型。
  创建一个新的WPF程序,这里还是用我熟悉的.net6,慎重思考1秒后决定命名为ServerControlSystem,新建的项目里面自带有MainWindow.xaml。
  Nuget上引用LayUI.Wpf和Prism的相关库:
5.png

   为了实现prism的自动关联,我们要新建两个文件夹,Views和ViewModels,将MainWindow.xaml移动到Views文件夹下,在ViewModels文件夹中对应新建MainWindowViewModel.cs:
6.png

   当然,也可以自定义,这就需要我们自己去修改App.xaml.cs,添加配置:
  1. protected override void ConfigureViewModelLocator()
  2. {
  3.     base.ConfigureViewModelLocator();
  4.     ViewModelLocationProvider.Register<MainWindow, MainWindowViewModel>();
  5. }
复制代码
  这里我们还是用的自动关联,因为移动了MainWindow的位置,所以要对应修改下命名空间的路径(不想改你就直接删了重建吧):
  MainWindow.xaml:
  1. <Window x:Class="ServerControlSystem.Views.MainWindow"...>
  2. ...
  3. </Window>
复制代码
  MainWindow.xaml.cs:
  1. public partial class MainWindow : Window
复制代码
  而在prism框架下,一般要将App的基类改为PrismApplication:
  App.xaml:
7.gif
8.gif
  1. <prism:PrismApplication x:Class="ServerControlSystem.App"
  2. <Lay:LayTitleBar.Header>
  3.     <Border Height="40">
  4.     </Border>
  5. </Lay:LayTitleBar.Header>     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  6. <Lay:LayTitleBar.Header>
  7.     <Border Height="40">
  8.     </Border>
  9. </Lay:LayTitleBar.Header>     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  10. <Lay:LayTitleBar.Header>
  11.     <Border Height="40">
  12.     </Border>
  13. </Lay:LayTitleBar.Header>     xmlns:local="clr-namespace:ServerControlSystem"
  14.     xmlns:prism="http://prismlibrary.com/">
  15.    
  16. <Lay:LayTitleBar.Header>
  17.     <Border Height="40">
  18.     </Border>
  19. </Lay:LayTitleBar.Header><ResourceDictionary>
  20. <Lay:LayTitleBar.Header>
  21.     <Border Height="40">
  22.     </Border>
  23. </Lay:LayTitleBar.Header>    <ResourceDictionary.MergedDictionaries>
  24. <Lay:LayTitleBar.Header>
  25.     <Border Height="40">
  26.     </Border>
  27. </Lay:LayTitleBar.Header><Lay:LayTitleBar.Header>
  28.     <Border Height="40">
  29.     </Border>
  30. </Lay:LayTitleBar.Header><ResourceDictionary Source="pack://application:,,,/LayUI.Wpf;component/Themes/Default.xaml" />
  31. <Lay:LayTitleBar.Header>
  32.     <Border Height="40">
  33.     </Border>
  34. </Lay:LayTitleBar.Header>    </ResourceDictionary.MergedDictionaries>
  35. <Lay:LayTitleBar.Header>
  36.     <Border Height="40">
  37.     </Border>
  38. </Lay:LayTitleBar.Header></ResourceDictionary>
  39.     </Application.Resources>
  40. </prism:PrismApplication>
复制代码
View Code  App.xaml.cs:
  1. public partial class App : PrismApplication
复制代码
  同时在prism框架下,一般是通过重写CreateShell方法来指定主窗口,且需要实现继承的RegisterTypes:
9.gif
10.gif
  1. public partial class App : PrismApplication
  2. {
  3.     protected override Window CreateShell()
  4.     {
  5. <Lay:LayTitleBar.Header>
  6.     <Border Height="40">
  7.     </Border>
  8. </Lay:LayTitleBar.Header>return Container.Resolve<MainWindow>();
  9.     }
  10.     protected override void OnStartup(StartupEventArgs e)
  11.     {
  12. <Lay:LayTitleBar.Header>
  13.     <Border Height="40">
  14.     </Border>
  15. </Lay:LayTitleBar.Header>base.OnStartup(e);
  16. <Lay:LayTitleBar.Header>
  17.     <Border Height="40">
  18.     </Border>
  19. </Lay:LayTitleBar.Header>DispatcherUnhandledException += App_DispatcherUnhandledException;
  20.     }
  21.     private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
  22.     {
  23. <Lay:LayTitleBar.Header>
  24.     <Border Height="40">
  25.     </Border>
  26. </Lay:LayTitleBar.Header>LayMessage.Error(e.Exception.Message);
  27. <Lay:LayTitleBar.Header>
  28.     <Border Height="40">
  29.     </Border>
  30. </Lay:LayTitleBar.Header>//记录日志
  31. <Lay:LayTitleBar.Header>
  32.     <Border Height="40">
  33.     </Border>
  34. </Lay:LayTitleBar.Header>e.Handled = true;
  35.     }
  36.     protected override void RegisterTypes(IContainerRegistry containerRegistry)
  37.     {
  38. <Lay:LayTitleBar.Header>
  39.     <Border Height="40">
  40.     </Border>
  41. </Lay:LayTitleBar.Header>//注入自定义接口
  42. <Lay:LayTitleBar.Header>
  43.     <Border Height="40">
  44.     </Border>
  45. </Lay:LayTitleBar.Header>LayDialog.Register(Container.Resolve<IContainerExtension>());
  46.     }
  47. }
复制代码
View Code  现在,我们可以开始快乐搬运了,先小搬一下MainWindow.xaml,这里我们只是验证控件库的调用,所以就试一下LayayTitleBar:
11.gif
12.gif
  1. <Window x:Class="ServerControlSystem.Views.MainWindow"
  2.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. <Lay:LayTitleBar.Header>
  5.     <Border Height="40">
  6.     </Border>
  7. </Lay:LayTitleBar.Header>xmlns:Lay="clr-namespace:LayUI.Wpf.Controls;assembly=LayUI.Wpf"
  8. <Lay:LayTitleBar.Header>
  9.     <Border Height="40">
  10.     </Border>
  11. </Lay:LayTitleBar.Header>xmlns:prism="http://prismlibrary.com/"
  12.     Width="1080"
  13.     Height="600"
  14.     prism:ViewModelLocator.AutoWireViewModel="True"
  15.     AllowsTransparency="True"
  16.     WindowStartupLocation="CenterScreen"
  17.     WindowStyle="None"
  18.     Title="MainWindow" >
  19.     <Grid>
  20. <Lay:LayTitleBar.Header>
  21.     <Border Height="40">
  22.     </Border>
  23. </Lay:LayTitleBar.Header><Lay:LayTitleBar
  24. <Lay:LayTitleBar.Header>
  25.     <Border Height="40">
  26.     </Border>
  27. </Lay:LayTitleBar.Header>    Background="{DynamicResource LighCyan}"
  28. <Lay:LayTitleBar.Header>
  29.     <Border Height="40">
  30.     </Border>
  31. </Lay:LayTitleBar.Header>    CornerRadius="4"
  32. <Lay:LayTitleBar.Header>
  33.     <Border Height="40">
  34.     </Border>
  35. </Lay:LayTitleBar.Header>    ResizeMode="CanResize"
  36. <Lay:LayTitleBar.Header>
  37.     <Border Height="40">
  38.     </Border>
  39. </Lay:LayTitleBar.Header>    WindowState="{Binding WindowState, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"><Lay:LayTitleBar.Header>
  40.     <Border Height="40">
  41.     </Border>
  42. </Lay:LayTitleBar.Header>   
  43. <Lay:LayTitleBar.Header>
  44.     <Border Height="40">
  45.     </Border>
  46. </Lay:LayTitleBar.Header></Lay:LayTitleBar>
  47.     </Grid>
  48. </Window>
复制代码
View Code  然后再看模型,先实现基类ViewModelBase:
13.gif
14.gif
  1. public abstract class ViewModelBase : BindableBase, INavigationAware, IRegionMemberLifetime, IConfirmNavigationRequest
  2. {
  3.     /// <summary>
  4.     /// 导航器
  5.     /// </summary>
  6.     public IRegionManager Region;
  7.     /// <summary>
  8.     /// 弹窗服务
  9.     /// </summary>
  10.     public IDialogService Dialog;
  11.     /// <summary>
  12.     /// 事件聚合器
  13.     /// </summary>
  14.     public IEventAggregator Event;
  15.     public ViewModelBase()
  16.     {
  17.     }
  18.     public ViewModelBase(IContainerExtension container)
  19.     {
  20. <Lay:LayTitleBar.Header>
  21.     <Border Height="40">
  22.     </Border>
  23. </Lay:LayTitleBar.Header>this.Region = container.Resolve<IRegionManager>();
  24. <Lay:LayTitleBar.Header>
  25.     <Border Height="40">
  26.     </Border>
  27. </Lay:LayTitleBar.Header>this.Dialog = container.Resolve<IDialogService>();
  28. <Lay:LayTitleBar.Header>
  29.     <Border Height="40">
  30.     </Border>
  31. </Lay:LayTitleBar.Header>this.Event = container.Resolve<IEventAggregator>();
  32.     }
  33.     private DelegateCommand _LoadedCommand;
  34.     public DelegateCommand LoadedCommand =>
  35. <Lay:LayTitleBar.Header>
  36.     <Border Height="40">
  37.     </Border>
  38. </Lay:LayTitleBar.Header>_LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand));
  39.     /// <summary>
  40.     ///初始化界面加载
  41.     /// </summary>
  42.     public virtual void ExecuteLoadedCommand()
  43.     {
  44.     }
  45.     /// <summary>
  46.     /// 控制视图是否被缓存
  47.     /// </summary>
  48.     public bool KeepAlive => false;
  49.     public virtual void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
  50.     {
  51. <Lay:LayTitleBar.Header>
  52.     <Border Height="40">
  53.     </Border>
  54. </Lay:LayTitleBar.Header>continuationCallback(true);
  55.     }
  56.     /// <summary>
  57.     /// 控制实例是否被缓存
  58.     /// </summary>
  59.     /// <param name="navigationContext"></param>
  60.     /// <returns></returns>
  61.     public virtual bool IsNavigationTarget(NavigationContext navigationContext)
  62.     {
  63. <Lay:LayTitleBar.Header>
  64.     <Border Height="40">
  65.     </Border>
  66. </Lay:LayTitleBar.Header>return false;
  67.     }
  68.     /// <summary>
  69.     /// 导航离开当前ViewModel时被调用
  70.     /// </summary>
  71.     /// <param name="navigationContext"></param>
  72.     public virtual void OnNavigatedFrom(NavigationContext navigationContext)
  73.     {
  74.     }
  75.     /// <summary>
  76.     /// 导航到当前ViewModel时被调用
  77.     /// </summary>
  78.     /// <param name="navigationContext"></param>
  79.     public virtual void OnNavigatedTo(NavigationContext navigationContext)
  80.     {
  81.     }
  82. }
复制代码
View Code  再实现MainWindowViewModel:
15.gif
16.gif
  1. public class MainWindowViewModel : ViewModelBase, IWindowAware
  2. {
  3.     private WindowState _WindowState;
  4.     /// <summary>
  5.     /// 窗体状态
  6.     /// </summary>
  7.     public WindowState WindowState
  8.     {
  9. <Lay:LayTitleBar.Header>
  10.     <Border Height="40">
  11.     </Border>
  12. </Lay:LayTitleBar.Header>get { return _WindowState; }
  13. <Lay:LayTitleBar.Header>
  14.     <Border Height="40">
  15.     </Border>
  16. </Lay:LayTitleBar.Header>set { _WindowState = value; RaisePropertyChanged(); }
  17.     }
  18.     public MainWindowViewModel(IContainerExtension container) : base(container)
  19.     {
  20.     }
  21.     public override void ExecuteLoadedCommand()
  22.     {
  23. <Lay:LayTitleBar.Header>
  24.     <Border Height="40">
  25.     </Border>
  26. </Lay:LayTitleBar.Header>base.ExecuteLoadedCommand();
  27.     }
  28.     public bool CanClosing()
  29.     {
  30. <Lay:LayTitleBar.Header>
  31.     <Border Height="40">
  32.     </Border>
  33. </Lay:LayTitleBar.Header>var res = MessageBox.Show("确定关闭窗体吗?", "提示", MessageBoxButton.OKCancel);
  34. <Lay:LayTitleBar.Header>
  35.     <Border Height="40">
  36.     </Border>
  37. </Lay:LayTitleBar.Header>if (res == MessageBoxResult.OK) return true;
  38. <Lay:LayTitleBar.Header>
  39.     <Border Height="40">
  40.     </Border>
  41. </Lay:LayTitleBar.Header>else return false;
  42.     }
  43. }
复制代码
View Code  到这一步基本就完成了,编译生成一下,运行后就能得到一个简陋的空界面:
17.png

   之后,就可以根据需要,自己添加控件了。比如,你可以加上,标题栏就会美观一点:
  1. <Lay:LayTitleBar.Header>
  2.     <Border Height="40">
  3.     </Border>
  4. </Lay:LayTitleBar.Header>
复制代码
四、总结

  Winform真好写。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册