抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

优势

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>

DearXuan
对于初学者,可以使用拖动的方式布局,对于高级开发者,可以前往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句柄。

命名空间: 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即为渐变画笔,以下图为例
DearXuan
渐变方向为(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;

这是效果
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
使用命令

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();

评论