如何實現WPF登陸介面的記住賬號密碼功能()
剛開始學程式設計。學了三個月了,學的C#和.net(貌似有點坑),公司應該是讓做b/s,socket和資料庫比較難,暫時不怎麼會,對WPF花的時間比較多一些(畢竟視覺化效果看起來比價有成就感)(WPF的課程案例貌似不多,加油)
新手菜鳥,大神勿噴。我覺得幾個有趣的地方,左上角的【HIM是畫的,因為這樣縮放清晰度一樣,刺激。
<Polygon Fill="DarkSlateGray" Stroke="DarkSlateGray" StrokeThickness="1" FillRule="EvenOdd" Points="5,5 67,5 62,13 25,13 25,47 62,47 67,55 5,55"/>
圖片那個是把一個TabControl控制元件翻轉了180°
<TabControl.RenderTransform>
<RotateTransform Angle="180"/>
</TabControl.RenderTransform>
好了,接下來看如何記住賬號密碼功能。主要是兩個文字框,一個是textbox,另一個是passwordbox,還有一個記住密碼的勾選框
<TextBox Name="txt_UserName" Text="{Binding Path=SaveLoginID}" HorizontalAlignment="Center" VerticalAlignment="Bottom" Width="200" Height="40"></TextBox>
<PasswordBox Name="txt_PSW" PasswordChar="*" local:PasswortBoxHelper.Attach="True" local:PasswortBoxHelper.Password="{Binding Path=SaveLoginPSW,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="40"></PasswordBox>
<CheckBox Name="LoginIDMemory" IsChecked="{Binding Path=SaveUser}">記住密碼</CheckBox>
可以將控制元件的值儲存在視窗中,但是這樣做之後缺乏靈活性,會將應用程式資料與UI介面混在一起,並不是一種好的設計。我們應該新建一個類,並將使用者所做的選擇儲存在其中。
(1)新建一個類,將其命名為SaveOptions
(2)我們要記住CheckBox,TextBox,PasswordBox的值,輸入以下程式碼
using System;
namespace DataEntity
{
[Serializable]
public class SaveOptions
{
private bool _SaveUser = false;
public bool SaveUser
{
get { };
set { };
}
private string _SaveLoginID;
public string SaveLoginID
{
get { };
set { };
}
private string _SaveLoginPSW;
public string SaveLoginPSW
{
get { };
set { };
}
}
}
(3)返回程式碼隱藏檔案(我的是 登陸.xaml.cs),新增一個private欄位來儲存saveOptions例項;並在建構函式中新增以下程式碼。
using System.Windows;
using System.IO;
using System.Xml.Serialization;
namespace CfyDWS
{
/// <summary>
/// 登入.xaml 的互動邏輯
/// </summary>
public partial class LoginWindow : Window
{
private SaveOptions saveOptions;
public LoginWindow()
{
if (saveOptions == null)
{
//確定指定的檔案是否存在
if (File.Exists("SaveOptions.xml"))
{
using (var stream = File.OpenRead("SaveOptions.xml"))
{
var serializer = new XmlSerializer(typeof(SaveOptions));
saveOptions = serializer.Deserialize(stream) as SaveOptions;
}
}
else
saveOptions = new SaveOptions();
}
InitializeComponent();
}
}
接下來建立動態繫結
(4)在InitializeComponent();這一行之前加上一下程式碼:
DataContext = saveOptions;
(5)轉到SaveOptions類,對其進行修改
using System;
using System.ComponentModel;
namespace DataEntity
{
[Serializable]
public class SaveOptions : INotifyPropertyChanged
{
private bool _SaveUser = false;
public bool SaveUser
{
get { return _SaveUser; }
set
{
_SaveUser = value;
OnPropertyChanged(nameof(SaveUser));
}
}
private string _SaveLoginID;
public string SaveLoginID
{
get { return _SaveLoginID; }
set
{
_SaveLoginID = value;
OnPropertyChanged(nameof(SaveLoginID));
}
}
private string _SaveLoginPSW;
public string SaveLoginPSW
{
get { return _SaveLoginPSW; }
set
{
_SaveLoginPSW = value;
OnPropertyChanged(nameof(SaveLoginPSW));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
(6)返回登陸.xaml檔案,選擇CheckBox,然後新增IsChenk屬性,如下所示:
IsChecked="{Binding Path=SaveUser}"
(7)同理繫結TextBoX控制元件的Text屬性
Text="{Binding Path=SaveLoginID}"
(8)繫結PasswordBox的Password屬性時會出現一個錯誤,PasswordBox的Password屬性不是依賴項屬性,無法繫結。
其原因是,出於安全考慮,對密碼進行資料庫不是一個很好的設計,因此應該避免。但是有時這種安全性是不必要的,那麼不能繫結到Password屬性就很麻煩了。在這種特殊情況下,您可以利用以下PasswordBoxHelper。程式碼如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace CfyDWS
{
public static class PasswortBoxHelper
{
//通過呼叫PasswordHelper.Attach財產。附加財產PasswordHelper.Password提供PasswordBox控制元件的原始密碼屬性的可繫結副本。
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.RegisterAttached("Password",
typeof(string), typeof(PasswortBoxHelper),
new FrameworkPropertyMetadata(string.Empty, OnPasswordPropertyChanged));
public static readonly DependencyProperty AttachProperty =
DependencyProperty.RegisterAttached("Attach",
typeof(bool), typeof(PasswortBoxHelper), new PropertyMetadata(false, Attach));
private static readonly DependencyProperty IsUpdatingProperty =
DependencyProperty.RegisterAttached("IsUpdating", typeof(bool),
typeof(PasswortBoxHelper));
public static void SetAttach(DependencyObject dp, bool value)
{
dp.SetValue(AttachProperty, value);
}
public static bool GetAttach(DependencyObject dp)
{
return (bool)dp.GetValue(AttachProperty);
}
public static string GetPassword(DependencyObject dp)
{
return (string)dp.GetValue(PasswordProperty);
}
public static void SetPassword(DependencyObject dp, string value)
{
dp.SetValue(PasswordProperty, value);
}
private static bool GetIsUpdating(DependencyObject dp)
{
return (bool)dp.GetValue(IsUpdatingProperty);
}
private static void SetIsUpdating(DependencyObject dp, bool value)
{
dp.SetValue(IsUpdatingProperty, value);
}
private static void OnPasswordPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
passwordBox.PasswordChanged -= PasswordChanged;
if (!(bool)GetIsUpdating(passwordBox))
{
passwordBox.Password = (string)e.NewValue;
}
passwordBox.PasswordChanged += PasswordChanged;
}
private static void Attach(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
if (passwordBox == null)
return;
if ((bool)e.OldValue)
{
passwordBox.PasswordChanged -= PasswordChanged;
}
if ((bool)e.NewValue)
{
passwordBox.PasswordChanged += PasswordChanged;
}
}
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
SetIsUpdating(passwordBox, true);
SetPassword(passwordBox, passwordBox.Password);
SetIsUpdating(passwordBox, false);
}
}
}
檢視登陸.xaml視窗,看到有名稱空間CfyDWS的引用,沒有的話自己手動新增,要引用CfyDWS名稱空間下的PasswordBoxHelper類的方法。
<Window x:Class="CfyDWS.LoginWindow"
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:CfyDWS"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Title="登入視窗" Icon="image\昌恆圖示.png" Height="780" Width="1460" ResizeMode="CanMinimize">
然後修改PasswordBox的屬性
<PasswordBox Name="txt_PSW" PasswordChar="*"
local:PasswortBoxHelper.Attach="True" local:PasswortBoxHelper.Password="{Binding Path=SaveLoginPSW, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></PasswordBox>
這裡說下看到的另一個方法,用TextBox替換PasswordBox,然後用TextDecorations屬性畫小黑點裝飾TextBox,這種感覺視窗大小最好固定吧。而且小圓點的大小不好把握,也可能是我這裡學的差。
(9)我把儲存選項放在登陸按鈕上了
這裡我是當按下確定時,假如賬號密碼不對,未勾選記住密碼,則清空賬號密碼框,如果正確,則登入
這裡主要看using()的那一段程式碼段
private void btn_login_Click(object sender, RoutedEventArgs e)
{
SqlConnectionStringBuilder connbuilder = new SqlConnectionStringBuilder();
connbuilder.DataSource = Environment.MachineName; //獲取伺服器名稱,本機為Cheng-PC(data ource資料來源,一般為機器名稱或IP地址)
connbuilder.IntegratedSecurity = true; //登入方式,true為 Windows 身份驗證
connbuilder.InitialCatalog = "昌恆DWS系統"; //資料庫名(Initial Catalog資料庫或SQL Server例項的名稱(與Database一樣))
SqlConnection conn = new SqlConnection(connbuilder.ConnectionString); //建立一個SqlConnecting物件並且初始化連線字串
conn.Open(); //開啟資料庫連線
string a = string.Format("select * from 賬戶管理 where login_ID='{0}' and login_PSW='{1}' ", txt_UserName.Text, txt_PSW.Password);
//定義要執行的SQL語句(本處是查詢語句)
SqlCommand comm = new SqlCommand(a, conn); //建立SqlCommand的例項物件(string a表示要查詢的文字,SqlConnecting conn表示到SQL Server例項的連線)
SqlDataReader dr = comm.ExecuteReader(); //ExecuteReader(),讀取資料,生成一個SqlDataReader物件並返回
if (txt_UserName.Text == "" || txt_PSW.Password == "") //判斷輸入是否為空
{
MessageBox.Show("請填寫使用者名稱和密碼");
using (var stream = File.Open("SaveOptions.xml", FileMode.Create))
{
if (LoginIDMemory.IsChecked == false)
{
saveOptions.SaveLoginID = "";
saveOptions.SaveLoginPSW = "";
}
var serializer = new XmlSerializer(typeof(SaveOptions));
serializer.Serialize(stream, saveOptions);
}
}
else
{
if (dr.Read())//判斷是否存在使用者輸入的使用者名稱和密碼
{
MessageBox.Show("登陸成功"); // 顯示主視窗;
this.DialogResult = Convert.ToBoolean(1);
using (var stream = File.Open("SaveOptions.xml", FileMode.Create))
{
if (LoginIDMemory.IsChecked == false)
{
saveOptions.SaveLoginID = "";
saveOptions.SaveLoginPSW = "";
}
var serializer = new XmlSerializer(typeof(SaveOptions));
serializer.Serialize(stream, saveOptions);
}
conn.Close(); //關閉資料庫連線
this.Close();
}
else
{
MessageBox.Show("使用者名稱或密碼有誤");
using (var stream = File.Open("SaveOptions.xml", FileMode.Create))
{
if (LoginIDMemory.IsChecked == false)
{
saveOptions.SaveLoginID = "";
saveOptions.SaveLoginPSW = "";
}
var serializer = new XmlSerializer(typeof(SaveOptions));
serializer.Serialize(stream, saveOptions);
}
conn.Close(); //關閉資料庫連線
}
}
}
這裡的using程式碼段也可以寫進一個void方法裡,直接呼叫方法
using (var stream = File.Open("SaveOptions.xml", FileMode.Create))
{
if (LoginIDMemory.IsChecked == false)
{
saveOptions.SaveLoginID = "";
saveOptions.SaveLoginPSW = "";
}
var serializer = new XmlSerializer(typeof(SaveOptions));
serializer.Serialize(stream, saveOptions);
}
``