前往我的博客以获得更好的阅读体验UWP编程基础 - DearXuan的主页https://blog.dearxuan.com/2021/10/05/UWP%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80/
优势
UWP即windows通用平台,用于创建可以运行在所有Windows10以上设备的应用程序。与传统exe应用比起来,UWP应用拥有更严格的权限系统,更美观的操作界面,更强大的自定义控件以及更方便的自适应布局。
界面布局
与Android类似,UWP应用采用XAML作为布局文件
<Page
x:Class="MailSystem_UWP.View.LoginPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MailSystem_UWP.View"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Height="475" Width="354">
<Grid Height="325" VerticalAlignment="Center" HorizontalAlignment="Center" Width="288">
<Grid.RowDefinitions>
<RowDefinition Height="288*"/>
<RowDefinition Height="79*"/>
</Grid.RowDefinitions>
<TextBlock Margin="10,70,0,0" Text="用户名" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Width="42" Height="19"/>
<TextBox x:Name="text1" Margin="10,94,10,0" Text="" TextWrapping="Wrap" VerticalAlignment="Top" KeyDown="onKeyDown_1" Height="32"/>
<TextBlock HorizontalAlignment="Left" Margin="10,156,0,0" Text="密码" TextWrapping="Wrap" VerticalAlignment="Top" Height="19" Width="28"/>
<PasswordBox x:Name="text2" Margin="10,180,10,0" VerticalAlignment="Top" InputScope="Password" Password="" KeyDown="onKeyDown_2" Height="32"/>
<Button x:Name="button_login" Content="登录" VerticalAlignment="Bottom" Click="onLoginClick" Margin="0,0,10,10" RenderTransformOrigin="0.131,-0.19" HorizontalAlignment="Right" Height="32" Width="66" Grid.Row="1"/>
<TextBlock x:Name="label1" HorizontalAlignment="Left" Margin="10,131,0,0" Foreground="Red" Text="请输入用户名" TextWrapping="Wrap" VerticalAlignment="Top" Visibility="Collapsed" Height="19" Width="84"/>
<TextBlock x:Name="label2" HorizontalAlignment="Left" Margin="10,217,0,0" Foreground="Red" Text="请输入密码" TextWrapping="Wrap" VerticalAlignment="Top" Visibility="Collapsed" Height="19" Width="70"/>
<TextBlock HorizontalAlignment="Center" Margin="0,10,0,0" Text="登录到MailSystem" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="22" Height="29" Width="177"/>
<Button x:Name="button_reg" Content="注册" VerticalAlignment="Bottom" Click="onRegClick" Margin="0,0,212,10" RenderTransformOrigin="0.131,-0.19" HorizontalAlignment="Right" Height="32" Width="66" Grid.Row="1"/>
</Grid>
</Page>
对于初学者,可以使用拖动的方式布局,对于高级开发者,可以前往XAML 概述学习XAML语法,因为许多自定义样式,画笔,布局都是无法通过拖动实现的
异步任务与UI线程
当用户点击一个按钮,系统自动生成一个消息,并插入到UI消息队列中,UI线程处理了这个消息,响应了点击事件。如果在点击事件中进行联网或文件读写等耗时操作,就会导致接下来的消息被阻塞,UI线程无法处理后面的消息,造成界面卡死。
Thread类
使用Thread进行多线程编程,其命名空间为System.Threading。
static void Main(string[] args)
{
Thread thread = new Thread(cal);
thread.Start();
}
public static void cal()
{
Console.Write("123");
Console.ReadLine();
}
修改thread.IsBackground属性来决定线程运行在前台还是后台,前后台的区别是:当前台线程结束,无论后台线程是否执行完成,都会被强制结束。因此后台线程适合用来监听,而不是保存数据。应用程序的主线程和new Thread()创建的线程默认都是前台线程,如果这些线程都结束,程序随即退出。
Task
使用Task.Run方法在线程池上创建新的后台线程,并返回Task<TResult>句柄。
命名空间: System.Threading.Tasks
例如,在后台进行登录操作
Task.Run(() => _Login(username, password));
异步方法
使用Task可以在后台执行操作,并返回结果,但是当前线程仍然会被Task中的代码阻塞,使用async修饰的异步方法,允许方法中断,并在后台线程结束后从中断处继续执行。
private async void onLoginClick(object sender, RoutedEventArgs e)
{
//获取用户名和密码,以便异步调用
string username = text1.Text;
string password = text2.Password;
//按钮不可用
SetAvailable(false);
//异步执行检查代码,防止UI线程卡死
await Task.Run(() => _Login(username, password));
//按钮可用
SetAvailable(true);
}
在检测登录信息前,将按钮设置为不可用状态,使用await修饰的Task语句,将检测登录信息的函数放在后台执行,并中断当前代码,当_Login方法结束时,程序从中断处继续执行,将按钮设置为可用。
使用该方法不会阻塞onLoginClick()所在的线程,因此不会造成界面卡死。
在后台更新UI
为了在后台线程中更新UI,需要将代码切换至UI线程执行,使用
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Priority, () => { action(); })
方法将action()插入UI消息队列中,并由UI线程执行。值得注意的是,一旦lambda表达式里的代码开始执行,该函数就会立即返回,因此不应该在lambda表达式中进行需要等待的操作,例如请求用户输入。
为了方便调用,我已经写好了Invoke()方法,你可以直接复制下面的代码
public async static void Invoke(Action action, CoreDispatcherPriority Priority = CoreDispatcherPriority.Normal)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Priority, () => { action(); });
}
Invoke()方法接收一个函数,并在UI线程执行。
示例代码如下
Invoke(() =>
{
MessageDialog dialog = new MessageDialog("内容")
{
Title = "标题"
};
dialog.Commands.Add(new UICommand("好的"));
dialog.DefaultCommandIndex = 0;
dialog.CancelCommandIndex = 1;
dialog.ShowAsync();
});
页面与跳转
右键解决方案-添加-新建项,选择空白页,即可新建页面。
创建JumpTo方法和OnBackClick方法
public static void JumpTo(Type page)
{
Frame root = Window.Current.Content as Frame;
Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += OnBackClick;
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = root.CanGoBack ? AppViewBackButtonVisibility.Visible : Windows.UI.Core.AppViewBackButtonVisibility.Collapsed;
root.Navigated += OnNavigated;
root.Navigate(page);
}
private static void OnBackClick(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
return;
if (rootFrame.CanGoBack && e.Handled == false)
{
e.Handled = true;
rootFrame.GoBack();
}
}
JumpTo()方法用于跳转至指定页面,并在左上角创建返回按钮,点击返回按钮后执行OnBackClick()里的代码。之后你就可以使用
JumpTo(typeof(MyPage));
来跳转到指定页面。
画笔
使用自定义画笔可以修改控件的样式,例如边框颜色。
纯色画笔
纯色画笔可以指定绘制某一种颜色。先定义颜色color为绿色
private static Color color = new Color()
{
A = 255,
R = 0,
G = 255,
B = 0
};
注意Color的命名空间是Windows.UI,而不是System.Drawing。
定义纯色画笔,并使用color初始化
public static SolidColorBrush brush = new SolidColorBrush(color);
应用画笔
text1.Foreground = brush;
渐变画笔
渐变画笔用于绘制包含渐变颜色的界面
定义LinearGradientBrush与GradientStop
LinearGradientBrush brush = new LinearGradientBrush();
GradientStop gradientStop1 = new GradientStop();
GradientStop gradientStop2 = new GradientStop();
LinearGradientBrush即为渐变画笔,以下图为例
渐变方向为(0,0)到(1,1),即起点和终点,那么向量(1,1)即是渐变向量。为了描述该向量,需要定义向量的起点与终点,并在向量上的不同地方定义不同的颜色。定义颜色需要用到GradientStop,我们称GradientStop为梯度点
现在初始化梯度点,并修改背景画笔
gradientStop1.Color = new Windows.UI.Color() { A = 255, R = 255, G = 0, B = 0 };
gradientStop1.Offset = 0;
gradientStop2.Color = new Windows.UI.Color() { A = 255, R = 0, G = 0, B = 255 };
gradientStop2.Offset = 1;
brush.GradientStops.Add(gradientStop1);
brush.GradientStops.Add(gradientStop2);
brush.StartPoint = new Point(0, 0);
brush.EndPoint = new Point(1, 0);
grid.Background = brush;
这是效果
Color是梯度点的颜色,Offset规定了梯度点在整个渐变向量中的位置,范围为0~1。如果你输入0.5,那么画笔的前50%是渐变图案,后50%是纯色,就像下面这张图
如果你输入2,那么画笔就会有一半画到窗体外面,也就是说只有前50%的画笔有效,比如下面这张图,你看不到蓝色,因为蓝色被画到外面了
StartPoint和EndPoint分别是画笔相对于绘制区域的起点和终点坐标,即是渐变向量。x,y轴的正方向分别是向右和向下,(0,0)~(1,1)是默认区域,这是斜向下的渐变。
为了实现垂直方向的渐变,需要定义向量(0,1),因此StartPoint为(0,0),EndPoint为(0,1)
起始点和终点也可以小于1,如果它们分别为(0,0),(0.5,0.5),这说明画笔只对整个Page的左上角有效。如下图
左下方和右上角颜色不是纯色,这是因为之前的渐变向量填充了这里的颜色,而右下角不受渐变向量的控制,因此是纯蓝色
MySQL数据库
安装MySql包
在VS的下方打开“程序包管理器控制台”
如果没有则转到“视图”->“其他窗口”->“程序包管理器控制台”,第一次打开需要初始化,一般在5秒左右
使用命令
Install-Package MySql.Data
安装MySQL驱动
建立连接
定义连接语句
private const string DATABASE_SERVER = "localhost";
private const string DATABASE_NAME = "dearxuan";
private const string DATABASE_USER = "root";
private const string DATABASE_PASSWORD = "root";
private const string DATABASE_PORT = "3306";
private const string SQL_CONNECTION_STR =
"server=" + DATABASE_SERVER +
";user=" + DATABASE_USER +
";database=" + DATABASE_NAME +
";port=" + DATABASE_PORT +
";password=" + DATABASE_PASSWORD +
";SslMode=None";
定义连接,命令,结果集
private static MySqlConnection connection;
private static MySqlCommand command;
private static MySqlDataReader reader;
读取数据
//建立连接
connection = new MySqlConnection(SQL_CONNECTION_STR);
connection.Open();
//执行语句
command = new MySqlCommand(“SELECT * FROM dearxuan”, connection);
//获取结果集
reader = command.ExecuteReader();
while (reader.read())
{
//读取第一项和第二项
string username = reader[0].ToString();
string password = reader[1].ToString();
}
SQL注入防御
使用预编译的方法来预防SQL注入攻击。预编译语句不包含数据的值,并且会在填入值之前进行语法分析,之后填入的值即使包含了SQL关键字也仍然会被当成字符串处理
在SQL语句中以"@"开头的字符串代替原本值的位置,并使用AddWithValue()来替换值
string sql = "SELECT * FROM dearxuan WHERE username=@username AND password=@password"
command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("username", username);
command.Parameters.AddWithValue("password", password);
reader = command.ExecuteReader();