WPF应用程序(.NET Core)项目创建+登录布局+登录实现+wpf弹窗消息提示+wpf加载动画


WPF应用程序框架(.NET Core)

--此博客编写WPF为.net Core型,编写.net  Framework型参考博客--https://www.cnblogs.com/ZhuMeng-Chao/p/16362541.html

--WPF教程视频可以参考 https://www.bilibili.com/video/BV1nY411a7T8?p=1&spm_id_from=pageDriver

1、创建程序

 2、创建成功初始信息

---wpf所含文件(文件夹建立想法)

--只是在当下完成对于功能建立的文件夹--后续可扩充

 3、配置基础设施

3-1:App.xaml引入materialDesign ui资源 (类似于引用element ui)

3-1-1    安装包MaterialDesignThemes

3-1-2    App.xaml中代码引入materialDesign ui资源

xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"

            
            
                <materialDesign:BundledTheme
                    BaseTheme="Light"
                    PrimaryColor="DeepPurple"
                    SecondaryColor="Lime" />
                "pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
            
        
 3-1-3  应用materialDesign ui资源效果(类似用element ui控件)

 3-1-3   加入工具--实现代码格式化

 https://zhuanlan.zhihu.com/p/345257435

4、引入prism框架

4-1 默认没有注入之类的  --所以引入第三方架构prism

4-1-1  安装prism  (prism.Dryioc包)

4-1-2  代码引入prism

xmlns:prism="http://prismlibrary.com/"

4-2  配置上prism框架

4-2-1  App.xaml.cs文件中修改其基类

PrismApplication

4-2-2  修改基类后又对应错误(未配置)信息

4-2-3   解决 1:        其.cs文件对应的.xaml文件也应该修改

--未解决完错误信息:

4-2-4   解决 2:        其加入对应代码

 1  protected override Window CreateShell()
 2         {
 3             //默认启动页面
 4             return Container.Resolve();
 5         }
 6 
 7         /// 
 8         /// prism的视图和model的映射
 9         /// 
10         protected override void RegisterTypes(IContainerRegistry containerRegistry)
11         {
12             ////注册 view 与 viewmodel 的映射关系
13             //containerRegistry.RegisterForNavigation();
14 
15             ////注册 home 与 homeViewModel 的映射关系
16             //containerRegistry.RegisterForNavigation();
17         }

4-3  创建对应登录文件完成登录---主要在讲解如何创建wpf所需窗体

4-3-1  创建 .xaml 文件

 4-3-2 创建其对应ViewModel.cs 文件

  目的:编写.xaml要操作的方法(增删改查)对应按钮点击事件 和 实现类似与vue的双向绑定

 4-3-3   在Appp.xaml.cs中将两者(.xaml与其对应的.cs文件)建立起联系-关联(必须建立联系)

containerRegistry.RegisterForNavigation();

 4-3-4 创建一个项目基类--继承接口,实现数据双向绑定(后续项目每一个viewModel.cs类都需要继承基类)

1 public class Class1:BindableBase
2     {
3     }
baseViewModel

 

4-3-5  封装一个Http请求工具HttpRequestTools

  bs项目使用ajax请求webapi   cs项目使用httpclient请求webapi       =》 因为使用频繁所以直接封装一个工具,用的时候直接使用该工具就可

  1  public class HttpRequestTools
  2     {
  3         public static string apiHost = "http://localhost:5295/";
  4         public static string apiServicePrefixUrl = apiHost + "api/";
  5         public static string token = "";
  6         public static LoginUser loginUser = null;
  7         /// 
  8         /// 执行成功的返回方式
  9         /// 
 10         /// 返回结果类型
 11         /// 返回结果数据
 12         /// 返回结果信息
 13         /// 
 14         protected static ResultModel MyOk(T data, string mess = "执行成功")
 15         {
 16             ResultModel resultModel = new ResultModel();
 17             resultModel.data = data;
 18             resultModel.code = ResultCode.Ok;
 19             resultModel.mess = mess;
 20             return resultModel;
 21         }
 22         /// 
 23         /// 执行代码报错返回结果
 24         /// 
 25         /// 
 26         /// 
 27         /// 
 28         /// 
 29         protected static ResultModel MyError(Exception ex, string mess = "出现错误")
 30         {
 31             ResultModel resultModel = new ResultModel();
 32 
 33             resultModel.code = ResultCode.Error;
 34             resultModel.mess = mess + "错误信息是:" + ex.ToString();
 35             return resultModel;
 36         }
 37         /// 
 38         /// 执行没有达到预期的效果返回问题
 39         /// 
 40         /// 
 41         /// 
 42         /// 
 43         protected static ResultModel MyFail(string mess = "执行出现问题")
 44         {
 45             ResultModel resultModel = new ResultModel();
 46 
 47             resultModel.code = ResultCode.Fail;
 48             resultModel.mess = mess;
 49             return resultModel;
 50         }
 51 
 52         /// 
 53         /// 发起Get请求
 54         /// 
 55         /// 请求Url
 56         /// 请求参数
 57         /// 请求头部参数
 58         /// 请求内容类型
 59         /// 超时时间 单位:秒
 60         /// 
 61         public static ResultModel Get(string url, Dictionary<string, string> queryParams = null, Dictionary<string, string> headerParams = null, int timeOut = 30)
 62         {
 63 
 64             if (string.IsNullOrEmpty(url))
 65             {
 66                 return MyFail("Url不能是空的");
 67             }
 68             try
 69             {
 70                 using (HttpClient client = new HttpClient())
 71                 {
 72                     client.Timeout = new TimeSpan(0, 0, timeOut);
 73                     if (headerParams != null)
 74                     {
 75                         foreach (var header in headerParams)
 76                             client.DefaultRequestHeaders.Add(header.Key, header.Value);
 77                     }
 78 
 79                     string strQueryParams = "";
 80                     if (queryParams != null)
 81                     {
 82                         url = url + "?";
 83                         foreach (var param in queryParams)
 84                         {
 85                             url += param.Key + "=" + param.Value + "&";
 86                         }
 87                         url = url.TrimEnd('&');
 88                     }
 89 
 90                     string response = client.GetAsync(url).Result.Content.ReadAsStringAsync().Result;
 91                     if (!string.IsNullOrEmpty(response))
 92                         return JsonConvert.DeserializeObject>(response);  //序列化json
 93 
 94                     return MyFail("返回信息为null");
 95                 }
 96             }
 97             catch (Exception ex)
 98             {
 99                 return MyError(ex);
100             }
101 
102         }
103         /// 
104         /// 发起基于token的Get请求
105         /// 
106         /// 
107         /// 
108         /// 
109         /// 
110         public static ResultModel GetByToken(string method, Dictionary<string, string> queryParams = null, int timeOut = 30)
111         {
112             if (string.IsNullOrEmpty(token))
113             {
114                 return MyFail("没有请求的Token");
115             }
116             if (string.IsNullOrEmpty(method))
117             {
118                 return MyFail("method不能是空的");
119             }
120             string url = apiServicePrefixUrl + method;
121             Dictionary<string, string> header = new Dictionary<string, string>()
122             {
123                 { "mytoken",token }
124             };
125             return Get(url, queryParams, header, timeOut);
126         }
127         /// 
128         /// 发起基于token的Get请求,传入参数是指定类型
129         /// 
130         /// 
131         /// 
132         /// 
133         /// 
134         public static ResultModel GetByToken(string method, Params queryObj, int timeOut = 30)
135         {
136             Dictionary<string, string> queryParams = ObjectToMap(queryObj);
137             return GetByToken(method, queryParams, timeOut);
138         }
139         /// 
140         /// 发起Post请求
141         /// 
142         /// 请求url
143         /// 请求内容
144         /// 请求内容类型
145         /// 请求头部参数
146         /// 超时时间
147         /// 
148         public static ResultModel Post(string url, Params body, string contentType = "application/json", Dictionary<string, string> headerParams = null, int timeOut = 30)
149         {
150             if (string.IsNullOrEmpty(url))
151             {
152                 return MyFail("Url不能是空的");
153             }
154             string json = "";
155             if (body != null)
156             {
157                 json = JsonConvert.SerializeObject(body);
158             }
159             try
160             {
161                 using (HttpClient client = new HttpClient())
162                 {
163                     client.Timeout = new TimeSpan(0, 0, timeOut);
164                     if (headerParams != null)
165                     {
166                         foreach (var header in headerParams)
167                             client.DefaultRequestHeaders.Add(header.Key, header.Value);
168                     }
169                     using (HttpContent httpContent = new StringContent(json, Encoding.UTF8))
170                     {
171                         if (contentType != null)
172                             httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
173 
174                         string result = client.PostAsync(url, httpContent).Result.Content.ReadAsStringAsync().Result;
175                         if (!string.IsNullOrEmpty(result))
176                             return JsonConvert.DeserializeObject>(result);  //序列化json
177 
178                         return MyFail("返回信息为null");
179                     }
180                 }
181             }
182             catch (Exception ex)
183             {
184                 return MyError(ex);
185             }
186 
187         }
188         /// 
189         /// 发起带有token的Post请求
190         /// 
191         /// 
192         /// 
193         /// 
194         /// 
195         /// 
196         /// 
197         public static ResultModel PostByToken(string method, Params body, string contentType = "application/json", int timeOut = 30)
198         {
199             if (string.IsNullOrEmpty(token))
200             {
201                 return MyFail("没有请求的Token");
202             }
203             if (string.IsNullOrEmpty(method))
204             {
205                 return MyFail("method不能是空的");
206             }
207 
208             string url = apiServicePrefixUrl + method;
209             Dictionary<string, string> header = new Dictionary<string, string>()
210             {
211                 { "mytoken",token }
212             };
213             return Post(url, body, contentType, header, timeOut);
214 
215 
216         }
217         #region 对象转成字典
218         /// 
219         /// 对象转换为字典
220         /// 
221         /// 待转化的对象
222         /// 
223         public static Dictionary<string, string> ObjectToMap(object obj)
224         {
225             Dictionary<string, string> map = new Dictionary<string, string>();
226 
227             Type t = obj.GetType(); // 获取对象对应的类, 对应的类型
228 
229             PropertyInfo[] pi = t.GetProperties(BindingFlags.Public | BindingFlags.Instance); // 获取当前type公共属性
230 
231             foreach (PropertyInfo p in pi)
232             {
233                 MethodInfo m = p.GetGetMethod();
234                 if (m != null && m.IsPublic)
235                 {
236                     // 进行判NULL处理
237                     if (m.Invoke(obj, new object[] { }) != null)
238                     {
239                         map.Add(p.Name, m.Invoke(obj, new object[] { }).ToString()); // 向字典添加元素
240                     }
241                 }
242             }
243             return map;
244         }
245         #endregion
246 
247     }
HttpRequestTools

至此基本项目所需搭建成功


登录

4-3-4 登录页面显示布局

  4-3-4.1   实现登录时页面布局上有个图片(切记第三步那里改为资源,授课时说的容易不显示)

  4-3-4.2   页面布局1--图片显示

  4-3-4.2   页面布局2---实现有默认提示文字

      例如:布局文本框里面的提示文字--注意--需要申明一下这样布局

    

    完成基本布局  

    

   4-3-5  实现以弹出层方式运行该登录页面

  4-3-5.1  新建一个.xmal页和对应.cs文件并将其设置为起始页,然后运行开始通过dialog方式弹出登录弹出层

  --右键项目--添加--用户控件=》实现添加一个xmal页;并新建一个对应.cs文件;设置启示页+添加映射;

  4-3-5.2   App.xaml中编写初始化方法--实现项目启动的时候, 首先弹出登录框,进行登录

 1 /// 
 2         /// 初始化方法
 3         /// 
 4         protected override void OnInitialized()
 5         {
 6             //项目启动的时候, 首先弹出登录框,进行登录
 7             var dialog = Container.Resolve();
 8             dialog.ShowDialog("LoginView", callback =>
 9             {
10                 //如果选择的不是登录,则直接关闭程序
11                 if (callback.Result != ButtonResult.OK)
12                 {
13                     //关闭程序
14                     Environment.Exit(0);
15                     return;
16                 }
17                 //var service = App.Current.MainWindow.DataContext as IConfigureService;
18                 //if (service != null)
19                 //    service.Configure();
20                 base.OnInitialized();
21             });
22 
23         }

  异常报错:

   

  解决:

1、登录页引入dialog配置

1 
2         
9     
 xmlns:prism="http://prismlibrary.com/"

2、对应cs文件添加继承

--再次运行出现错误信息

解决---对应的继承实现方法最开始先按照如图红框里的编辑(目前先可正常运行)

实现窗口显示:

4、登录页布局完善

  1、点击登录系统按钮,有一个加载转圈的实现

  2、密码或者账号输入错误有对应信息弹出提示

实现一:

1  xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
2 
3 <md:DialogHost
4         x:Name="DialogHost"
5         DialogTheme="Inherit"
6         Identifier="Root">
7 

LoginView.xaml.cs文件使用prism 事件聚合器(https://www.cnblogs.com/ZhuMeng-Chao/p/16398225.html)对应“加载动画”事件实现加载转圈效果

 1 /// 
 2     /// LoginView.xaml 的交互逻辑
 3     /// 
 4     public partial class LoginView : UserControl
 5     {
 6         public LoginView(IEventAggregator aggregator, LoginViewModel view)
 7         {
 8             InitializeComponent();
 9             //使用事件聚合器 加载动画
10             aggregator.WaitForResgiter(arg =>
11             {
12                 Dispatcher.Invoke(new Action(() =>
13                 {
14                     DialogHost.IsOpen = arg.IsOpen;
15                     if (DialogHost.IsOpen)
16                         DialogHost.DialogContent = new ProgressView();
17                 }));
18             });
19             // DialogHost
20         }
21     }
WaitForResgiter 

创建一个放置加载转圈的那个控件xmal页面  --右键--新增--用户控件

1 "100">
2             <ProgressBar
3                 Width="40"
4                 Height="40"
5                 Margin="20"
6                 IsIndeterminate="True"
7                 Style="{StaticResource MaterialDesignCircularProgressBar}" />
8         
StackPanel

对应model.cs文件(LoginViewModel.cs)中对应按钮触发事件里编写使用事件聚合器里的事件

 完成效果

 实现二:

1  
2             <md:Snackbar
3                 x:Name="LoginSnackBar"
4                 Grid.ColumnSpan="2"
5                 Panel.ZIndex="1"
6                 MessageQueue="{md:MessageQueue}" />
Snackbar

 LoginView.xaml.cs文件使用prism 事件聚合器(https://www.cnblogs.com/ZhuMeng-Chao/p/16398225.html)对应“消息提示”事件实现弹出信息框显示

1  //使用事件聚合器 消息提示
2             aggregator.ResgiterMessage(arg =>
3             {
4                 Dispatcher.Invoke(new Action(() =>
5                 {
6                     LoginSnackBar.MessageQueue.Enqueue(arg.Message);
7                 }));
8             });

对应所需要弹框出编辑内容

实现效果

5、调用api接口实现登录

  

WPF