抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

优势

UWP即windows通用平台,用于创建可以运行在所有Windows10以上设备的应用程序.与传统exe应用比起来,UWP应用拥有更严格的权限系统,更美观的操作界面,更强大的自定义控件以及更方便的自适应布局.

界面布局

与Android类似,UWP应用采用XAML作为布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<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>

DearXuan
对于初学者,可以使用拖动的方式布局,对于高级开发者,可以前往XAML 概述学习XAML语法,因为许多自定义样式,画笔,布局都是无法通过拖动实现的

异步任务与UI线程

当用户点击一个按钮,系统自动生成一个消息,并插入到UI消息队列中,UI线程处理了这个消息,响应了点击事件.如果在点击事件中进行联网或文件读写等耗时操作,就会导致接下来的消息被阻塞,UI线程无法处理后面的消息,造成界面卡死.

Thread类

使用Thread进行多线程编程,其命名空间为System.Threading.

1
2
3
4
5
6
7
8
9
10
11
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句柄.

命名空间: System.Threading.Tasks

例如,在后台进行登录操作

1
Task.Run(() => _Login(username, password));

异步方法

使用Task可以在后台执行操作,并返回结果,但是当前线程仍然会被Task中的代码阻塞,使用async修饰的异步方法,允许方法中断,并在后台线程结束后从中断处继续执行.

1
2
3
4
5
6
7
8
9
10
11
12
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线程执行,使用

1
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Priority, () => { action(); })

方法将action()插入UI消息队列中,并由UI线程执行.值得注意的是,一旦lambda表达式里的代码开始执行,该函数就会立即返回,因此不应该在lambda表达式中进行需要等待的操作,例如请求用户输入.

为了方便调用,我已经写好了Invoke()方法,你可以直接复制下面的代码

1
2
3
4
public async static void Invoke(Action action, CoreDispatcherPriority Priority = CoreDispatcherPriority.Normal)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Priority, () => { action(); });
}

Invoke()方法接收一个函数,并在UI线程执行.

示例代码如下

1
2
3
4
5
6
7
8
9
10
11
Invoke(() =>
{
MessageDialog dialog = new MessageDialog("内容")
{
Title = "标题"
};
dialog.Commands.Add(new UICommand("好的"));
dialog.DefaultCommandIndex = 0;
dialog.CancelCommandIndex = 1;
dialog.ShowAsync();
});

页面与跳转

右键解决方案-添加-新建项,选择空白页,即可新建页面.

创建JumpTo方法和OnBackClick方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()里的代码.之后你就可以使用

1
JumpTo(typeof(MyPage));

来跳转到指定页面.

画笔

使用自定义画笔可以修改控件的样式,例如边框颜色.

纯色画笔

纯色画笔可以指定绘制某一种颜色.先定义颜色color为绿色

1
2
3
4
5
6
7
private static Color color = new Color() 
{
A = 255,
R = 0,
G = 255,
B = 0
};

注意Color的命名空间是Windows.UI,而不是System.Drawing.

定义纯色画笔,并使用color初始化

1
public static SolidColorBrush brush = new SolidColorBrush(color);

应用画笔

1
text1.Foreground = brush;

渐变画笔

渐变画笔用于绘制包含渐变颜色的界面

定义LinearGradientBrush与GradientStop

1
2
3
LinearGradientBrush brush = new LinearGradientBrush();
GradientStop gradientStop1 = new GradientStop();
GradientStop gradientStop2 = new GradientStop();

LinearGradientBrush即为渐变画笔,以下图为例
DearXuan
渐变方向为(0,0)到(1,1),即起点和终点,那么向量(1,1)即是渐变向量.为了描述该向量,需要定义向量的起点与终点,并在向量上的不同地方定义不同的颜色.定义颜色需要用到GradientStop,我们称GradientStop为梯度点

现在初始化梯度点,并修改背景画笔

1
2
3
4
5
6
7
8
9
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;

这是效果
DearXuan
Color是梯度点的颜色,Offset规定了梯度点在整个渐变向量中的位置,范围为0∼1.如果你输入0.5,那么画笔的前50%是渐变图案,后50%是纯色,就像下面这张图
DearXuan
如果你输入2,那么画笔就会有一半画到窗体外面,也就是说只有前50%的画笔有效,比如下面这张图,你看不到蓝色,因为蓝色被画到外面了
DearXuan
StartPoint和EndPoint分别是画笔相对于绘制区域的起点和终点坐标,即是渐变向量.x,y轴的正方向分别是向右和向下,(0,0)∼(1,1)是默认区域,这是斜向下的渐变.
DearXuan
为了实现垂直方向的渐变,需要定义向量(0,1),因此StartPoint为(0,0),EndPoint为(0,1)
DearXuan
起始点和终点也可以小于1,如果它们分别为(0,0),(0.5,0.5),这说明画笔只对整个Page的左上角有效.如下图
DearXuan
左下方和右上角颜色不是纯色,这是因为之前的渐变向量填充了这里的颜色,而右下角不受渐变向量的控制,因此是纯蓝色

MySQL数据库

安装MySql包

在VS的下方打开“程序包管理器控制台”

如果没有则转到“视图”->“其他窗口”->“程序包管理器控制台”,第一次打开需要初始化,一般在5秒左右
DearXuan
使用命令

1
Install-Package MySql.Data

来安装MySQL驱动

建立连接

定义连接语句

1
2
3
4
5
6
7
8
9
10
11
12
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";

定义连接,命令,结果集

1
2
3
private static MySqlConnection connection;
private static MySqlCommand command;
private static MySqlDataReader reader;

读取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//建立连接
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()来替换值

1
2
3
4
5
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();

评论