目录
4. 资源字典&资源合并
10. MD5加密
11. 随机汉字验证码
13. 数据表
14. 程序多开提醒
1. 效果图
2. 项目结构
3. LoginView.xaml
<Window x:Class="LoginManagement.View.LoginView"
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:local="clr-namespace:LoginManagement.View"
xmlns:common="clr-namespace:LoginManagement.Common"
mc:Ignorable="d"
Name="window"
Title="LoginView" Height="600" Width="360"
FontFamily="Microsoft YaHei" FontWeight="ExtraLight" ResizeMode="NoResize"
WindowStartupLocation="CenterScreen" WindowStyle="None" AllowsTransparency="True" Background="{x:Null}">
<Window.Resources>
<!--资源太多,未贴出-->
<Window.Resources>
<Border Margin="5" Background="White" CornerRadius="10" FocusManager.FocusedElement="{Binding ElementName=username}">
<!--阴影效果(DropShadowEffect)
Color : 阴影的颜色; ShadowDepth: 确定阴影离开内容的距离,0的时候是晕圈; Opacity : 阴影的透明效果,取值0~1
Direction : 阴影相对于内容的方向,取值0~360,0时在右侧,数值增加,逆时针转动
BlurRadius : 模糊半径,以像素为单位的正直,标识模糊开始处到边缘的距离-->
<Border.Effect>
<DropShadowEffect Color="Gray" ShadowDepth="0" BlurRadius="5" Opacity="0.3" Direction="0"/>
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1.8*"/>
<RowDefinition Height="3*"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<Border Background="#007DFA" CornerRadius="10,10,0,0" MouseLeftButtonDown="Border_MouseLeftButtonDown"/>
<Button VerticalAlignment="Top" HorizontalAlignment="Right" Style="{StaticResource WindowControlButtonTemplate}"
Content=""
Command="{Binding CloseWindowCommand}" CommandParameter="{Binding ElementName=window}"/>
<StackPanel VerticalAlignment="Bottom" Margin="0,0,0,20">
<Border Width="80" Height="120" Background="White" VerticalAlignment="Center" HorizontalAlignment="Center" CornerRadius="50" Margin="0,0,0,10">
<Border.Effect>
<DropShadowEffect Color="White" ShadowDepth="0" BlurRadius="5" Opacity="0.3" Direction="0"/>
</Border.Effect>
<Border Width="90" Height="120" HorizontalAlignment="Center">
<Border.Background>
<ImageBrush ImageSource="../Assets/Images/Login.jpg"/>
</Border.Background>
</Border>
</Border>
<TextBlock Text="WPF--------Learning" FontSize="18" Foreground="White" HorizontalAlignment="Center"/>
</StackPanel>
<Grid Grid.Row="1" Margin="20,5">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition MinHeight="20" Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Text="{Binding LoginModel.UserName, UpdateSourceTrigger=PropertyChanged}" Name="username" Height="42" FontSize="16" Foreground="#555" Style="{DynamicResource UserNameTextBoxStyle}">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=window}"/>
</TextBox.InputBindings>
</TextBox>
<PasswordBox Grid.Row="1" Height="42" FontSize="16" Foreground="#555" Style="{DynamicResource PasswordBoxStyle}"
common:PasswordHelper.Password="{Binding LoginModel.Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<PasswordBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=window}"/>
</PasswordBox.InputBindings>
</PasswordBox>
<TextBox Grid.Row="2" Text="{Binding LoginModel.ValidationCode, UpdateSourceTrigger=PropertyChanged}" Height="42" FontSize="16" Foreground="#555" Style="{DynamicResource VlidationCodeTextBoxStyle}">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=window}"/>
</TextBox.InputBindings>
</TextBox>
<DockPanel Grid.Row="3" >
<Button Height="42" Width="270" Content="登 录" Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=window}"
Template="{StaticResource LoginButtonTemplate}" Foreground="White" FontSize="16" IsDefault="True"/>
<Button Content="注 册" Width="35" Height="40" Command="{Binding RegisterCommand}" HorizontalAlignment="Right"
Template="{StaticResource LoginButtonTemplate}" Foreground="White" FontSize="13" />
</DockPanel>
<TextBlock Grid.Row="4" Text="{Binding LoginModel.ErrorMessage}" Foreground="Red" TextWrapping="Wrap" FontSize="13" LineHeight="22" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<Grid Grid.Row="2" Margin="20,0">
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="20"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border BorderBrush="#DDD" BorderThickness="0,0,0,1" VerticalAlignment="Center"/>
<Border Grid.Column="2" BorderBrush="#DDD" BorderThickness="0,0,0,1" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="OR" Foreground="#CCC" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<UniformGrid Columns="5" Grid.Row="1" >
<UniformGrid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#DDD"/>
<Setter Property="FontSize" Value="40"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontFamily" Value="../Assets/Fonts/#iconfont"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="#007DFA"/>
</Trigger>
</Style.Triggers>
</Style>
</UniformGrid.Resources>
<TextBlock Text=""/>
<Border/>
<TextBlock Text=""/>
<Border/>
<TextBlock Text=""/>
</UniformGrid>
</Grid>
</Grid>
</Border>
</Window>
4. 资源字典&资源合并
新建资源字典DefaultStyle.xaml,存放统一的样式资源
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LoginManagement.Assets.Styles">
<Style TargetType="Button" x:Key="WindowControlButtonTemplate">
<Setter Property="Width" Value="45"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="Transparent" Name="kilter">
<TextBlock Text="{Binding Content, RelativeSource={RelativeSource AncestorType=Button, Mode=FindAncestor} }"
HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="../Fonts/#iconfont"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="kilter" Property="Background" Value="#22FFFFFF"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="kilter" Property="Background" Value="#44FFFFFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
UI调用资源字典,资源合并
<Window.Resources>
<ResourceDictionary Source="../Assets/Styles/DefaultStyle.xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<!-- 此处为自身的资源样式-->
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
5. iconfont的使用
使用如下图的字体图标,跟字体一样可修改多种属性,简洁好用
② 注册账号,搜索需要的字体图标 ,选好相应图标后,点击添加入库
③ 再从库里添加至项目中,若无项目先新建
④ 进入到我的项目,如下图,复制图标下的代码(以供项目中使用)
⑤ 点击下载至本地,文件解压后,将其中的文件“iconfont.ttf”复制到项目中,
⑥ 调用 其中text值设置为相应图标代码;FontFamily设置为“iconfont.ttf”的路径
<TextBlock Text="" FontFamily="../Assets/Fonts/#iconfont"/>
6. 使用SqlSugar访问mysql数据库
① 先使用NuGet安装SqlSugar和Mysql.Data和Newtonsoft.Json
② App.config中配置mysql连接字符串,其中密码、数据库根据自身修改
<connectionStrings>
<add name="db" connectionString="server=127.0.0.1;user id=root;pwd=111;database=wpf_data;pooling=true;MaximumPoolSize=10;MinimumPoolSize=3;" />
</connectionStrings>
③MysqlDataAccess.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SqlSugar;
using System.Configuration;
using LoginManagement.DataAccess.DataEntity;
using LoginManagement.Common;
namespace LoginManagement.DataAccess
{
class MysqlDataAccess
{
public SqlSugarClient MysqlAccess { get; set; }
private static MysqlDataAccess instance;
public static MysqlDataAccess Instance
{
get
{
return instance?? (instance=new MysqlDataAccess(ConfigurationManager.ConnectionStrings["db"].ConnectionString,DbType.MySql));
}
}
private MysqlDataAccess(string connStr, DbType dbType)
{
MysqlAccess = new SqlSugarClient(new ConnectionConfig
{
ConnectionString = connStr,
DbType = dbType,
IsAutoCloseConnection = false
});
}
public SqlSugarClient GetNewdb()
{
string connStr = ConfigurationManager.ConnectionStrings["db"].ConnectionString;
SqlSugarClient newdb = new SqlSugarClient(new ConnectionConfig
{
ConnectionString = connStr,
DbType=DbType.MySql,
IsAutoCloseConnection=true
});
return newdb;
}
public bool CheckUserInfo(string userName,string pwd)
{
var isExist = MysqlAccess.Queryable<Users>().Any(it => it.User_name == userName && it.Password == Md5Provider.GetMd5String($"{userName}@{pwd}"));
return isExist;
}
public bool CheckUserValidation(string userName)
{
var info = MysqlAccess.Queryable<Users>().First(it => it.User_name == userName);
if (info.Is_validation==1) return true;
return false;
}
public bool CheckUserExist(string userName)
{
var isExist = MysqlAccess.Queryable<Users>().Any(it => it.User_name == userName);
if (isExist) return true;
return false;
}
public bool AddUser(string userName, string pwd)
{
Users newUser = new Users
{
User_name = userName,
//MD5密码加密, MD5对“用户名+@+密码”加密作为密码
Password = Md5Provider.GetMd5String($"{userName}@{pwd}"),
Is_validation=1,
};
var rtn= MysqlAccess.Insertable(newUser).AS("users").ExecuteCommand();
if (rtn == 1) return true;
return false;
}
}
}
7. 附加属性 PasswordHelper.cs
通过使用附加属性做为桥梁,对PasswordBox.Password属性进行更新
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace LoginManagement.Common
{
public class PasswordHelper
{
static bool _isUpdating = false;
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.RegisterAttached("Password",typeof(string),typeof(PasswordHelper),
new FrameworkPropertyMetadata("",new PropertyChangedCallback(OnPropertyChanged)));
public static string GetPassword(DependencyObject d)
{
return d.GetValue(PasswordProperty).ToString();
}
public static void SetPassword(DependencyObject d,string value)
{
d.SetValue(PasswordProperty,value);
}
private static void OnPropertyChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = d as PasswordBox;
passwordBox.PasswordChanged -= PasswordBox_PasswordChanged;
if (!_isUpdating)
passwordBox.Password = e.NewValue?.ToString();
passwordBox.PasswordChanged += PasswordBox_PasswordChanged;
}
private static void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
_isUpdating = true;
SetPassword(passwordBox, passwordBox.Password);
_isUpdating = false;
}
}
}
调用
<PasswordBox common:PasswordHelper.Password="{Binding LoginModel.Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</PasswordBox>
8. 命令&属性通知
命令: CommandBase.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace LoginManagement.Common
{
public class CommandBase : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return DoCanExecute?.Invoke(parameter)==true;
}
public void Execute(object parameter)
{
DoExecute?.Invoke(parameter);
}
public Action<object> DoExecute { get; set; }
public Func<object, bool> DoCanExecute { get; set; }
}
}
调用
class LoginViewModel
{
public CommandBase CloseWindowCommand { get; set; }
public CommandBase LoginCommand { get; set; }
public LoginViewModel()
{
InitialCommand();
}
private void InitialCommand()
{
CloseWindowCommand = new CommandBase
{
DoExecute = new Action<object>((o) =>
{
(o as Window).Close();
}),
DoCanExecute = new Func<object, bool>((o) => { return true; })
};
LoginCommand = new CommandBase
{
DoExecute = new Action<object>(DoLogin),
DoCanExecute = new Func<object, bool>((o) => { return true; })
};
}
}
属性修改通知: NotifyBase.cs
public class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
//[CallerMemberName] 可以自动获取属性名称,可以不用传递属性名做参数
public void DoNotify([CallerMemberName] string propName="")
{
PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propName));
}
}
调用:
public class LoginModel:NotifyBase
{
private string _userName;
public string UserName
{
get { return _userName; }
set
{
_userName = value;
DoNotify();
}
}
}
9. 重写启动
App.xaml.cs 先启动LoginView,登录成功后,设置其DialogResult值为true,再启动MainView
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (new LoginView().ShowDialog()==true)
{
new MainView().ShowDialog();
}
Application.Current.Shutdown();
}
}
App.xaml
<Application x:Class="LoginManagement.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LoginManagement"
ShutdownMode="OnExplicitShutdown">
<Application.Resources>
</Application.Resources>
</Application>
10. MD5加密
public class Md5Provider
{
public static string GetMd5String(string str)
{
MD5 mD5 = MD5.Create();
byte[] pws = mD5.ComputeHash(Encoding.UTF8.GetBytes(str));
string pwd = "";
foreach (var item in pws)
{
pwd += item.ToString("X2");
}
return pwd;
}
}
11. 随机汉字验证码
//随机生成常用的汉字
private string GenerateChineseWords()
{
string chineseWords = "";
Random rm = new Random();
Encoding gb = Encoding.GetEncoding("gb2312");
for (int i = 0; i < 4; i++)
{
// 获取区码(常用汉字的区码范围为16-55)
int regionCode = rm.Next(16, 55);
// 获取位码(位码范围为1-94 由于55区的90,91,92,93,94为空,故将其排除)
int positionCode;
if (regionCode == 55)
{
// 55区排除90,91,92,93,94
positionCode = rm.Next(1, 90);
}
else
{
positionCode = rm.Next(1, 95);
}
// 转换区位码为机内码
int regionCode_Machine = regionCode + 160;// 160即为十六进制的20H+80H=A0H
int positionCode_Machine = positionCode + 160;// 160即为十六进制的20H+80H=A0H
// 转换为汉字
byte[] bytes = new byte[] { (byte)regionCode_Machine, (byte)positionCode_Machine };
chineseWords += gb.GetString(bytes);
}
return chineseWords;
}
12. LoginViewModel.cs
登录\注册处理
class LoginViewModel
{
public LoginModel LoginModel { get; set; } = new LoginModel();
public CommandBase CloseWindowCommand { get; set; }
public CommandBase LoginCommand { get; set; }
public CommandBase ChangeValidationCodeCommand { get; set; }
public CommandBase RegisterCommand { get; set; }
public LoginViewModel()
{
InitialCommand();
LoginModel.OriginalValidationCode = GenerateChineseWords();
}
private void InitialCommand()
{
CloseWindowCommand = new CommandBase
{
DoExecute = new Action<object>((o) =>
{
(o as Window).Close();
}),
DoCanExecute = new Func<object, bool>((o) => { return true; })
};
LoginCommand = new CommandBase
{
DoExecute = new Action<object>(DoLogin),
DoCanExecute = new Func<object, bool>((o) => { return true; })
};
ChangeValidationCodeCommand = new CommandBase
{
DoExecute = new Action<object>((o) => { LoginModel.OriginalValidationCode = GenerateChineseWords(); }),
DoCanExecute = new Func<object, bool>((o) => { return true; })
};
RegisterCommand = new CommandBase
{
DoExecute=new Action<object>(DoRegister),
DoCanExecute=new Func<object, bool>((o)=> { return true; })
};
}
private void DoLogin(object o)
{
if (string.IsNullOrEmpty(LoginModel.UserName))
{
LoginModel.ErrorMessage = "用户名不能为空";
return;
}
if (string.IsNullOrEmpty(LoginModel.Password))
{
LoginModel.ErrorMessage = "密码不能为空";
return;
}
if (string.IsNullOrEmpty(LoginModel.ValidationCode))
{
LoginModel.ErrorMessage = "验证码不能为空";
return;
}
if (LoginModel.ValidationCode!= LoginModel.OriginalValidationCode)
{
LoginModel.ErrorMessage = "验证码错误";
return;
}
LoginModel.ErrorMessage = "";
Task.Run(new Action(()=>
{
try
{
bool isExist = MysqlDataAccess.Instance.CheckUserInfo(LoginModel.UserName, LoginModel.Password);
if (!isExist)
{
LoginModel.ErrorMessage = "用户名或者密码错误";
return;
}
bool is_validation = MysqlDataAccess.Instance.CheckUserValidation(LoginModel.UserName);
if (!is_validation)
{
LoginModel.ErrorMessage = "用户名已失效";
return;
}
Application.Current.Dispatcher.Invoke(new Action(()=>
{
(o as Window).DialogResult = true;
}));
}
catch (Exception ex)
{
LoginModel.ErrorMessage = ex.Message;
}
}));
}
private void DoRegister(object o)
{
if (string.IsNullOrEmpty(LoginModel.UserName))
{
LoginModel.ErrorMessage = "注册时,用户名不能为空";
return;
}
if (string.IsNullOrEmpty(LoginModel.Password))
{
LoginModel.ErrorMessage = "注册时,密码不能为空";
return;
}
Task.Run(new Action(()=>
{
try
{
var isExist = MysqlDataAccess.Instance.CheckUserExist(LoginModel.UserName);
if (isExist)
{
LoginModel.ErrorMessage = "用户名已被注册";
return;
}
var rtn= MysqlDataAccess.Instance.AddUser(LoginModel.UserName,LoginModel.Password);
if (rtn)
{
LoginModel.ErrorMessage = "注册成功";
return;
}
LoginModel.ErrorMessage = "注册异常,请重试";
}
catch (Exception ex)
{
LoginModel.ErrorMessage = ex.Message;
return;
}
}));
}
}
13. 数据表
14. 程序多开提醒
修改App.xaml.cs,在程序执行时进行进程判断,对是否已有程序执行做相应选项处理
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (EnsureInstance())
{
return;
}
if (new LoginView().ShowDialog()==true)
{
new MainView().ShowDialog();
}
Application.Current.Shutdown();
}
/// 该函数设置由不同线程产生的窗口的显示状态
/// </summary>
/// <param name="hWnd">窗口句柄</param>
/// <param name="cmdShow">指定窗口如何显示。查看允许值列表,请查阅ShowWlndow函数的说明部分</param>
/// <returns>如果函数原来可见,返回值为非零;如果函数原来被隐藏,返回值为零</returns>
[DllImport("User32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
/// <summary>
/// 该函数将创建指定窗口的线程设置到前台,并且激活该窗口。键盘输入转向该窗口,并为用户改各种可视的记号。
/// 系统给创建前台窗口的线程分配的权限稍高于其他线程。
/// </summary>
/// <param name="hWnd">将被激活并被调入前台的窗口句柄</param>
/// <returns>如果窗口设入了前台,返回值为非零;如果窗口未被设入前台,返回值为零</returns>
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
private const int SW_SHOWNOMAL = 1;
private static void HandleRunningInstance(Process instance)
{
ShowWindowAsync(instance.MainWindowHandle, SW_SHOWNOMAL);//显示
SetForegroundWindow(instance.MainWindowHandle);//当到最前端
}
private static Process[] RuningInstance()
{
Process currentProcess = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(currentProcess.ProcessName);
return processes.Where(x => x.Id != currentProcess.Id).ToArray();
}
private static bool EnsureInstance()
{
var intance = RuningInstance();
var select = false;
if (intance != null && intance.Count() > 0)
{
var result = MessageBox.Show(string.Format(CultureInfo.CurrentCulture,
$"软件已经在后台运行{intance.Count()}个实例!\r\n" +
$"路径:\r\n{GetProcessPath(intance)}\r\n\n" +
"请选择:\n" +
"1.关闭后台程序重新运行\t[是]\n" +
"2.显示后台程序\t\t[否]\n" +
"3.保留后台程序并启动新程序\t[取消]\n"),
"LoginManagement", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
switch (result)
{
case MessageBoxResult.Yes:
KillProcess(intance);
break;
case MessageBoxResult.No:
case MessageBoxResult.None:
Array.ForEach(intance, x => HandleRunningInstance(x));
select = true;
break;
case MessageBoxResult.Cancel:
break;
default:
break;
}
}
return select;
}
private static string GetProcessPath(Process[] processes)
{
var sb = new StringBuilder();
for (int i = 0; i < processes.Count(); i++)
{
sb.Append($"{i + 1}:{processes[i].MainModule.FileName}\n");
}
return sb.ToString();
}
private static void KillProcess(Process[] processes)
{
foreach (var p in processes)
{
p.Kill();
if (!p.HasExited)
{
MessageBox.Show(string.Format(CultureInfo.CurrentCulture, "进程关闭失败!请手动确认进程已关闭"),
"LoginManagement", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}
效果:二次打开exe时,报以下处理提醒
异常:
1. Navicat连接Mysql-8.0.25报“2059”错误,
原因:新版本的MySQL使用的是caching_sha2_password验证方式,但此时的navicat还没有支持这种验证方式。
解决方法:将验证方式改为以前版本(5.7及以下)使用的验证方式mysql_native_password。
执行命令:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '密码';
'root'可以改为你自己定义的用户名,'localhost'指的是该用户开放的IP,可以是'localhost'(仅本机访问,相当于127.0.0.1),可以是具体的'...'(具体某一IP),也可以时'%'(所有IP均可问)。'密码'是你想使用的验证密码。
github地址:https://github.com/KilterW/ManagementSystem.git
参考: 朝夕教育 :WPF 客户端管理系统 (qq.com)