刚开始学编程。学了三个月了,学的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:PasswordBoxHelper.Attach="True" local:PasswordBoxHelper.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 PasswordBoxHelper
{
//通过调用PasswordHelper.Attach财产。附加财产PasswordHelper.Password提供PasswordBox控件的原始密码属性的可绑定副本。
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.RegisterAttached("Password",
typeof(string), typeof(PasswordBoxHelper),
new FrameworkPropertyMetadata(string.Empty, OnPasswordPropertyChanged));
public static readonly DependencyProperty AttachProperty =
DependencyProperty.RegisterAttached("Attach",
typeof(bool), typeof(PasswordBoxHelper), new PropertyMetadata(false, Attach));
private static readonly DependencyProperty IsUpdatingProperty =
DependencyProperty.RegisterAttached("IsUpdating", typeof(bool),
typeof(PasswordBoxHelper));
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:PasswordBoxHelper.Attach="True" local:PasswordBoxHelper.Password="{Binding Path=SaveLoginPSW, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></PasswordBox>
这里说下看到的另一个方法,用TextBox替换PasswordBox,然后用TextDecorations属性画小黑点装饰TextBox,这种感觉窗口大小最好固定吧。而且小圆点的大小不好把握,也可能是我这里学的差。
(9)我把保存选项放在登陆按钮上了
这里我是当按下确定时
1.1 账号密码错误,未勾选记住密码 清空账号密码
1.2 账号密码错误,勾选记住密码 账号密码保存
1.3 账号密码正确,未勾选记住密码 清空账号密码,登录进入主窗口
1.4 账号密码正确,勾选记住密码 账号密码保存,登录进入主窗口
关于如何确定账号密码正确与如何进入主界面,参考引用如下
WPF 登陆界面判断输入的用户名和密码是否与数据库中保存的数据相同
https://blog.csdn.net/u010637394/article/details/43833139?utm_source=blogxgwz5
WPF:验证登录后关闭登录窗口,显示主窗口的解决方法
https://blog.csdn.net/Jacee0048/article/details/49952643?utm_source=blogxgwz3
这里主要看using()的那一段代码段,将using代码段写进一个void方法里
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("请填写用户名和密码");
SaveOptionsXml();
}
else
{
if (dr.Read())//判断是否存在用户输入的用户名和密码
{
MessageBox.Show("登陆成功"); // 显示主窗口;
this.DialogResult = Convert.ToBoolean(1);
SaveOptionsXml();
conn.Close(); //关闭数据库连接
this.Close();
}
else
{
MessageBox.Show("用户名或密码有误");
SaveOptionsXml();
conn.Close(); //关闭数据库连接
}
}
private void SaveOptionsXml()
{
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);
}
}
}
(10)查了半天实现了PasswordBox的水印功能(用的模板和样式)。参考如下
然后查TextBox的水印功能,感觉很麻烦,试过把账号登录框TextBox换成PasswordBox,这样用一个模板样式就可以了。但是发现TextBox换成PasswordBox会问题,账号框输入的文本会显示为小圆点。后面找了半天还是换成TextBox了。
上面网页样式的TextBox样式前面缺了一个缺一段键名TxbBase的样式,补上去就可以直接用了
Style x:Key="TxbBase" TargetType="TextBox">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="20"/>
<!--光标的颜色-->
<Setter Property="CaretBrush" Value="Green"/>
<!--屏蔽系统右键菜单-->
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Visibility="Collapsed"/>
</Setter.Value>
</Setter>
<!--用来禁用掉中文输入法-->
<!--<Setter Property="input:InputMethod.IsInputMethodEnabled" Value="False"/>-->
</Style>
(11)新建一个PasswordBoxHelper类,代码如下(接口可以删掉或保留,我用的vs2017)
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 PasswordBoxHelper
{
//通过调用PasswordHelper.Attach财产。附加财产PasswordHelper.Password提供PasswordBox控件的原始密码属性的可绑定副本。
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.RegisterAttached("Password",
typeof(string), typeof(PasswordBoxHelper),
new FrameworkPropertyMetadata(string.Empty, OnPasswordPropertyChanged));
public static readonly DependencyProperty AttachProperty =
DependencyProperty.RegisterAttached("Attach",
typeof(bool), typeof(PasswordBoxHelper), new PropertyMetadata(false, Attach));
private static readonly DependencyProperty IsUpdatingProperty =
DependencyProperty.RegisterAttached("IsUpdating", typeof(bool),
typeof(PasswordBoxHelper));
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);
}
}
}
(12)PasswordBox的样式如下:
与参考的区别主要是
- 命名空间name我的是local,参考是Core
- 样式触发器有所不同
<Trigger Property="local:PasswordBoxWaterMark.PasswordLength" Value="0">
<Setter TargetName="WaterMark" Property="Visibility" Value="Visible"/>
用参考里面的这里会出现一个问题,就是我选择了记住账号密码,然后登陆退出,重新打开程序,会发现记住的账号密码和水印都存在。
应换成如下代码,(看(9)中的using代码块)因为我没有选择记住账号密码的话,账号密码文本框内容会清空。账号密码框文本为空的时候出现水印才合理。
<Trigger Property="local:PasswordBoxHelper.Password" Value="">
<Setter TargetName="WaterMark" Property="Visibility" Value="Visible"/>
</Trigger>
完成代码如下
<Style TargetType="PasswordBox">
<!--输入文本框的字体的颜色,水印的颜色在grid空间里面的Textblock文本框中设置-->
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="20"/>
<!--光标的颜色-->
<Setter Property="CaretBrush" Value="Green"/>
<Setter Property="local:PasswordBoxWaterMark.IsMonitoring" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type PasswordBox}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<Grid>
<ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
<TextBlock x:Name="WaterMark" Focusable="False" Visibility="Collapsed" Text="{TemplateBinding Tag}" Foreground="Red"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Opacity="0.5"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="local:PasswordBoxHelper.Password" Value="">
<Setter TargetName="WaterMark" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
(13)TextBox样式如下:
文本框的颜色我设的黑色,水印设置为红色
<Style x:Key="TxbBase" TargetType="TextBox">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="20"/>
<!--光标的颜色-->
<Setter Property="CaretBrush" Value="Green"/>
<!--屏蔽系统右键菜单-->
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Visibility="Collapsed"/>
</Setter.Value>
</Setter>
<!--用来禁用掉中文输入法-->
<!--<Setter Property="input:InputMethod.IsInputMethodEnabled" Value="False"/>-->
</Style>
<!--触发器的形式进行水印效果-->
<Style x:Key="TxbTrigger" TargetType="TextBox" BasedOn="{StaticResource TxbBase}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<Grid>
<ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
<TextBlock x:Name="WaterMark" Focusable="False" Visibility="Collapsed" Text="{TemplateBinding Tag}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Opacity="0.5" Foreground="Red"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#FF569DE5"/>
</Trigger>
<Trigger Property="Text" Value="">
<Setter Property="Visibility" TargetName="WaterMark" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
(14)TextBox和PasswordBox空间完整代码如下:
<TextBox Name="txt_UserName" Margin="20,0,5,10" Tag="请输入账号" Style="{StaticResource TxbTrigger}" Text="{Binding Path=SaveLoginID}"
HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="40" SnapsToDevicePixels="True" VerticalContentAlignment="Center"></TextBox>
<PasswordBox Name="txt_PSW" Margin="20,0,5,0" PasswordChar="●" Tag="请输出密码"
local:PasswordBoxHelper.Attach="True" local:PasswordBoxHelper.Password="{Binding Path=SaveLoginPSW,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="40" SnapsToDevicePixels="True" VerticalContentAlignment="Center"></PasswordBox>
效果如图