C#之上位机开发---------C#通信库及WPF的简单实践

〇、上位机,分层架构

界面层

要实现的功能:
展示数据
获取数据
发送数据

数据层

要实现的功能:
转换数据
打包数据
存取数据

通信层

要实现的功能:
打开连接
关闭连接
读取数据
写入数据

实体类

作用:
封装数据、传递数据

工具类

一、通信介绍及简单测试

一、PLC (Programmable Logic Controller | 可编程逻辑控制器)

简介:

PLC的英文全称是"Programmable Logic Controller",中文称为“可编程逻辑控制器”。这是一种数字运算操作电子系统,专为在工业环境下应用而设计。它采用可编程存储器,用来在其内部存储执行逻辑运算、顺序控制、定时、计数和算术运算等操作的指令,并通过数字式或模拟式的输入和输出,控制各种类型的机械或生产过程。

3

1、操作:西门子 smart2000 ,使用工具进行通讯

VD 4byte
VW 2byte
VB 1byte
在这里插入图片描述

在这里插入图片描述
V102.0 读一1bit
VB102 读1byte
VW102 读2byte
VD102 读4byte

二、Modbus

Modbus是一种通信协议,主要用于工业电子设备之间进行数据交换。

通讯的模型:
在这里插入图片描述

1、模拟测试(TCP)

所需软件:
mbpoll.exe
mbslave.exe

在这里插入图片描述

激活码:

注册码 对 7和 6 都可以使用

poll 注册码

5A5742575C5D10

slave 注册码

5455415451475662

建立 slave,即服务端 (poll,客户端也是类似的)

进行连接 :
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
设置连接信息
在这里插入图片描述

收发信息的具体情况:

在这里插入图片描述
在这里插入图片描述

1-2、模拟测试(串口)

所需软件:
在这里插入图片描述
在这里插入图片描述
创建虚拟串口对:

在这里插入图片描述
建立 主站 poll
在这里插入图片描述
在这里插入图片描述

建立从站 slave
在这里插入图片描述

在这里插入图片描述

2、存储区、存储区代码、范围

在这里插入图片描述
注:在这里布尔和线圈是一个意思:即一位的数据
在这里插入图片描述
注:一个区的空间为: 65536 每个为 2byte 大(short)

3、关于读写的功能码

在这里插入图片描述

4、协议分类

在这里插入图片描述
注:ModbusASCII因为速度慢,很少被使用
ModbusRTU、ModbusASCII 一般用串口
ModbusTCP 一般用以太网

5、ModbusRTU协议:

在这里插入图片描述

举个例子:
在这里插入图片描述

6、ModbusTCP

在这里插入图片描述
在这里插入图片描述
注:Tx的最后4个字节:00 00 00 02,00 00 表示起始 , 而 00 02 表示读两个字节

三、串口

简介:

一位一位的发送数据(以协上好的频率(波特率)和格式)

格式:
在这里插入图片描述

9针 串口:
在这里插入图片描述

分类:
RS-232
短距离通信

RS-422
长距离通信

RS-485
折中,
通常在半双工的模式下工作
RS-485标准理论上支持长达1200米的传输距离

单工: 类似,广播
半双工: 类似,对讲机
全双工: 类似,电话

测试:虚拟串口

二、C# 通信库的使用

1、s7通信库

1、举例:写一个C#与s7的通信

(1)、所需软件:

S7-PLCSIM Advanced V.30
TIA Portal V17

VD 4byte
VW 2byte
VB 1byte

(2)、界面:

在这里插入图片描述

(3)、添加所需库:

S7netplus
thinger.DataConvertLib
在这里插入图片描述

在这里插入图片描述

(4)、代码:
<1>.简单 测试下 连接-读写
using S7.Net;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            Test();
        }



        Plc plc = null;
        private void Test()
        {
            plc = new Plc(CpuType.S7200Smart, "192.168.2.1", 0, 0);
            plc.Open();

            //读取数据
            object data = plc.Read("M20.0");
            this.label1.Text = data.ToString();


            //写入数据
            plc.Write("M20.0", false);

            //不支持V区直接操作,需要映射成DB1
            plc.Write("DB1.DBX2000.0", true);

            plc.Close();
        }
    }
}

<2>.简单 的封装下
using S7.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WindowsFormsApp1
{
    public class S7NetLib
    {
        private Plc s7netlib = null;    //字段

        //属性
        public CpuType CPUType { get;set; }
        public string IPAddress { get; set; }
        public short Rack { get; set; }
        public short Slot { get; set; }
        //构造函数,初始化连接 所需的变量
        public S7NetLib(CpuType cpuType,string ip,short rack,short slot)
        {
            this.CPUType = cpuType;
            this.IPAddress = ip;
            this.Rack = rack;
            this.Slot = slot;
        }
        /// <summary>
        /// 打开PLC连接
        /// </summary>
        public void OpenPLC()
        {
            if(this.s7netlib == null)
            {
                s7netlib = new Plc(CPUType,IPAddress,Rack,Slot);
            }
            if (!this.s7netlib.IsConnected)
            {
                s7netlib.ReadTimeout = 1000;//设置超时时间
                s7netlib.WriteTimeout = 1000;
                s7netlib.Open();//建立连接
            }
        }
        /// <summary>
        /// 关闭PLC连接
        /// </summary>
        public void ClosePLC()
        {
            if(null != this.s7netlib && this.s7netlib.IsConnected)
            {
                this.s7netlib.Close();
            }
        }
        /// <summary>
        /// 给plc单个变量写入数据
        /// </summary>
        /// <param name="varAddress">写到那里去</param>
        /// <param name="varValue">写入的值</param>
        public void WriteDataToPLC(string varAddress, object varValue)
        {
            OpenPLC();
            lock (this)
            {
                this.s7netlib.Write(varAddress, varValue);
            }
        }
        /// <summary>
        /// 读取一段数据
        /// </summary>
        /// <param name="dataType">存储区类型</param>
        /// <param name="db">DB号</param>
        /// <param name="startByteAdr">开始字节地址</param>
        /// <param name="count">字节数量</param>
        /// <returns>字节数组</returns>
        public byte[] ReadDataFromPLC(DataType dataType,int db,int startByteAdr,int count)
        {
            lock (this)
            {
                byte[] bytes = this.s7netlib.ReadBytes(dataType,db,startByteAdr,count);
                return bytes;
            }
        }
    }
}

using S7.Net;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            Test();
        }

        private void Test()
        {
            S7NetLib plc = new S7NetLib(CpuType.S7200, "192.168.2.1", 0, 0);

            plc.WriteDataToPLC("M2.2", true);

            byte[] dataBytes = null;
            dataBytes = plc.ReadDataFromPLC(DataType.DataBlock, 1, 0, 10);
        }
    }
}

一次读一个PDU 的长度,不同 CPU的 PDU 的长度不同
在这里插入图片描述

2、C# + SQLSERVER

〇、环境

软件的安装(服务器端、客户端)

服务器端:
SQL Sever 下载地址:
https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

客户端
在这里插入图片描述

服务器端的操作:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

客户端的操作:

在这里插入图片描述
在这里插入图片描述

两种连接方式:
在这里插入图片描述

SQL Server 的连接配置(增加使用密码登录的用户):

第一步:
在这里插入图片描述
在这里插入图片描述
第二步:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
第三步: 重新启动,在连接登录
在这里插入图片描述.

开启远程用户登录的方式(使用 IP 和 端口号)

第一步:
在这里插入图片描述

在这里插入图片描述

第二步:
在这里插入图片描述
第三步:重启服务
右键我的电脑,点击属性
在这里插入图片描述

第四步:最后登录
在这里插入图片描述

一、操作软件:

SQL Server Management Studio

在这里插入图片描述

1、两种连接方式:

在这里插入图片描述

2、新建表

在这里插入图片描述
在这里插入图片描述
然后 ctrl+s 保存

3、添加数据

在这里插入图片描述

4、查询数据

在这里插入图片描述
注:注释是在前面加 –
在这里插入图片描述

5、解决不允许保存的弹窗

在这里插入图片描述

6、设置主键

在这里插入图片描述

7、更改数据(增加、删除、需改)
  --查
  select * from UserT

  --存
  --新增
  insert into UserT(UserName,Password,NickName) values('111','222','333')

  --删除
  delete from UserT where UserName='111'
  delete from UserT where UserName='111' and Password='888'

  --修改
  update UserT set UserName='a' where UserName='111'

到某个指定的数据库

use QingTongXiaWaterPlant_test
go

修改某一段名的数据类型:

use QingTongXiaWaterPlant_test
go
alter table dbo.WaterFlowData alter column d17 float null;

二、数据库数据类型

在这里插入图片描述

三、数据库的约束

在这里插入图片描述

四、运算符:

=

五、SQL 语句

(1)、搜索当前存在哪些数据库:
select * from sysdatabases
(2)、创建数据库:

1、创建数据库所在的文件夹
在这里插入图片描述
建好的文件
在这里插入图片描述

2、执行 sql语句

use master
go
if exists(select * from sysdatabases where name='MISDB') --如果原来存在这个数据库,则进行删除
drop database MISDB
go
--创建数据库
create database MISDB
on primary
(
	name='MISDB_MData',--必须唯一
	filename='D:\DB\MISDB_MData.mdf', --物理文件名,主存储文件
	size=30MB,filegrowth=10MB
)
,
(
	name='MISDB_nData',
	filename='D:\DB\DBMISDB_nData.ndf',  --次存储文件
	size=20MB,
	filegrowth=10MB
)
log on
(
	name='MISDB_log1',
	filename='D:\DB\MISDB_log1.ldf',	--日志文件
	size=20MB,
	filegrowth=10MB
)
,
(
	name='MISDB_log2',
	filename='D:\DB\MISDB_log2.ldf',		--日志文件
	size=20MB,
	filegrowth=10MB
)

(3)、创建表:
--创建数据表,是在指定的数据库里面
use MISDB
go
if exists(select * from sysobjects where name='Department')	  --如果已经有了 Department 表则对其进行删除
drop table Department
go
create table Department
(
	DepartmentId int identity(10,1)primary key,--部门字段值,由系统自动生成,从10开始,每次增加1  primary key 是主键的标识
	DepartmentName varchar(50)not null
)
go
if exists(select * from sysobjects where name='Post')		--如果已经有了 Post 表则对其进行删除
drop table Post
go
create table Post
(
	PostId int identity(10,1)primary key,
	PostName varchar(50) not null
)
go
if exists(select * from sysobjects where name='Employee')
drop table Employee
go
create table Employee
(
	EmplyeeId int identity(100,1) primary key,
	EmplyeeName varchar(50) not null,
	Gender char(2) not null check(Gender='男' or Gender='女'),
	NowAddress nvarchar(100) default('地址不详'),
	IdNo char(18) not null check(len(Idno)=18),--检查约束
	WeiXinNumber varchar(20)not null,
	PhoneNumber varchar(50) not null,
	OtherWork nvarchar(50) not null,
	EntryDate datetime not null,
	PostId int references Post(PostId),                    --外键引用
	DepartmentId int references Department(DepartmentId)   --外键引用
)
go
(4)、简单的 增、删、改、查
  --查
  select * from UserT

  --存
  --新增
  insert into UserT(UserName,Password,NickName) values('111','222','333')

  --删除
  delete from UserT where UserName='111'
  delete from UserT where UserName='111' and Password='888'

  --修改
  update UserT set UserName='a' where UserName='111'

到某个指定的数据库

use QingTongXiaWaterPlant_test
go

修改某一段名的数据类型:

use QingTongXiaWaterPlant_test
go
alter table dbo.WaterFlowData alter column d17 float null;
(5)、增加
use MISDB
go
select * from Department
select * from Post
select * from Employee

insert into Department(DepartmentName)
values('开发部'),('测试部'),('财务部'),('人事部')
inSert into Post(PostName)
values('软件工程师'),('测试工程师'),('实施工程师'),('财务经理'),('人事经理')
insert into Employee(EmployeeName,Gender,NowAddress,IdNo,
WeiXinNumber,PhoneNumber,OtherWork,EntryDate,PostId,DepartmentId)values
('Kiter10','男','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter11','男','北京','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter12','男','福州','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter13','男','西安','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter14','男','苏州','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter15','男','咸阳','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter16','男','永寿','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter17','女','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter18','男','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter19','男','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter20','男','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter21','女','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter22','男','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter23','男','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter24','男','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter25','女','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter26','男','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12),
('Kiter27','男','天津','123456789123456789','tt00','36954215478','没有','2024-09-09',10,12)
(6)、删除
delete from Employee where EmployId=112
delete from Employee where EmployId>=117
(7)、修改
update Employee set EmployeeName='小王',NowAddress='天津X'where EmployId=101
(8)、查询(及内查询)
select * from Department
select * from Post
select * from employee

--条件查询
select EmployId,EmployeeName,Gender,NowAddress,PhoneNumber
from Employee where EmployId>=105 and EmployId<=115 and gender='女'

update Employee set EmployeeName='小王',NowAddress='天津X'where EmployId=101
delete from Employee where EmployId=112
delete from Employee where EmployId>=117


--内连接查询
select EmployId,EmployeeName,PhoneNumber,Post.PostId,Post.PostName
from Employee
inner join Post on Post.PostId=Employee.PostId

--内连接查询
select EmployId,EmployeeName,PhoneNumber,
Post.PostId,PostName,DepartmentName
from Employee
inner join Post on Post.PostId=Employee.PostId
inner join Department on Department.DepartmentId=Employee.DepartmentId

--聚合查询
select count(*) as 员工总数 from Employee
select 编号平均数=avg(EmployId)from Employee
select 编号最小值=min(EmployId)from Employee
select 编号最大值=max(EmployId)from Employee
(9)、给表增加列:
ALTER TABLE Employees
ADD 
    Column1 INT,
    Column2 NVARCHAR(50),
    Column3 DATETIME;    
(10)、存储过程:

新建

CREATE PROCEDURE JiaYao
	-- 输入参数 执行哪个加药
	@Index varchar(32) ='',
	@C1_DangLiang real=0,
	-- 输出
	@dosage real output
AS
BEGIN
    -- 为了不返回 每条sql 影响多少条记录的信息
    SET NOCOUNT ON 
	
	select 
	avg(data_js_d1)   as js_cod,
	from RealData
	
	if @Index='PAC1'
	begin	
		set @dosage =js_cod/3;
	end
	if @Index='PAC2'
	begin	
		set @dosage =js_cod/2;
	end

END 

修改

ALTER PROCEDURE JiaYao
	-- 输入参数 执行哪个加药
	@Index varchar(32) ='',
	@C1_DangLiang real=0,
	-- 输出
	@dosage real output
AS
BEGIN
    -- 为了不返回 每条sql 影响多少条记录的信息
    SET NOCOUNT ON 
	
	select 
	avg(data_js_d1)   as js_cod,
	from RealData
	
	if @Index='PAC1'
	begin	
		set @dosage =js_cod/3;
	end
	if @Index='PAC2'
	begin	
		set @dosage =js_cod/2;
	end

END 

执行的sql


DECLARE @dosage real;

EXEC JiaYao @dosage=1.3,
	-- 输入参数 执行哪个加药
	@Index ='PAC1',

六、在C#中 使用,SQLServer 数据库

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Management.Instrumentation;

namespace ConsoleApp1
{
    public class SqlServer
    {
        /**         建立连接所需要的信息
         * Server   是服务器的地址
         * DataBase 是数据库的名称
         * Uid      是登录的用户名
         * Pwd      是用户名的密码
         */
        //private string connString1 = "Server=E2JMKGABJ62SR4X\\SQLEXPRESS;DataBase=MISDB;Uid=sa;Pwd=123456";
        //private string connString1 = "Server=192.168.31.130,1433\\SQLEXPRESS;DataBase=MISDB;Uid=sa;Pwd=123456";
        private string connString1 = "Server=192.168.31.130,1433;DataBase=MISDB;Uid=sa;Pwd=123456";

        //建立连接的方法
        public void ConnectDB()
        {
            SqlConnection conn = new SqlConnection(connString1);
            conn.Open();
            if (conn.State == System.Data.ConnectionState.Open)
            {
                Console.WriteLine("连接成功");
            }
            conn.Close();
            if (ConnectionState.Closed == conn.State)
            {
                Console.WriteLine("连接关闭");
            }
        }
        //插入语句的写法
        public void Insert()
        {
            //创建建立连接的对象 -- SqlConnection
            SqlConnection conn = new SqlConnection(connString1);
            //sql语句,
            string sql = "insert into Employee(EmployeeName,Gender,NowAddress,IdNo,WeiXinNumber,PhoneNumber,OtherWork,EntryDate,PostId,DepartmentId)Values('Kiter30','女','天津','123456789123456789','uio001','96587112365','没有的','2024-10-06',10,12)";
            //创建执行 sql 语句的对象
            SqlCommand cmd = new SqlCommand(sql, conn);
            //连接
            conn.Open();
            //执行sql语句
            int result = cmd.ExecuteNonQuery();
            //断开连接
            conn.Close();
            Console.WriteLine("受影响的行数:"+result);
        }

        //变更数据的写法
        public void Update()
        {
            //创建建立连接的对象 -- SqlConnection
            SqlConnection conn = new SqlConnection(connString1);
            //sql语句
            string sql = "update Employee set EmployeeName='UBM'where EmployId=121";
            //创建执行 sql 语句的对象
            SqlCommand cmd = new SqlCommand(sql, conn);
            //连接
            conn.Open();
            //执行sql语句
            int result = cmd.ExecuteNonQuery();
            //断开连接
            conn.Close();
            
            Console.WriteLine("受影响的行数:" + result);
        }

        //删除表中的记录
        public void Delete()
        {
            //创建建立连接的对象 -- SqlConnection
            SqlConnection conn = new SqlConnection(connString1);
            //要执行的 sql 语句
            string sql = "delete from Employee where EmployId=102";
            //实例化 要执行 sql的对象 -- SqlCommand
            SqlCommand cmd = new SqlCommand(sql, conn);
            //建立连接
            conn.Open();
            //执行 sql语句
            int result = cmd.ExecuteNonQuery();
            //关闭练级
            conn.Close();
            
            Console.WriteLine("受影响的行数:" + result);
        }

        //执行查询结果为1个的 sql 语句
        public void GetSingleResult()
        {
            //创建建立连接的对象 -- SqlConnection
            SqlConnection conn = new SqlConnection(connString1);
            //要执行的 sql 语句
            string sql = "select EmployeeName from Employee where EmployId=101";
            //实例化 要执行 sql的对象 -- SqlCommand
            SqlCommand cmd = new SqlCommand(sql, conn);
            //建立连接
            conn.Open();
            //执行 sql语句  ExecuteScalar 是执行只有一个返回结果的sql 语句
            object result = cmd.ExecuteScalar();
            //关闭连接
            conn.Close();
            
            Console.WriteLine(result);
        }
        //执行查询结果为1个的 sql 语句
        public void GetSingleResult2()
        {
            //创建建立连接的对象 -- SqlConnection
            SqlConnection conn = new SqlConnection(connString1);
            //要执行的 sql 语句
            string sql = "select 员工总数=count(*)from Employee";
            //实例化 要执行 sql的对象 -- SqlCommand
            SqlCommand cmd = new SqlCommand(sql, conn);
            //建立连接
            conn.Open();
            //执行 sql语句  ExecuteScalar 是执行只有一个返回结果的sql 语句
            object result = cmd.ExecuteScalar();

            int count = (int)result;//如果程序需要使用具体数据类型,就可以转换
            //关闭连接
            conn.Close();
            Console.WriteLine(count);
        }

        //用 ExecuteScalar 来执行 插入操作,返回看新增的记录是第几条的
        public void GetSingleResult3()
        {
            SqlConnection conn = new SqlConnection(connString1);
            string sql = "insert into Employee(EmployeeName,Gender," +
                "NowAddress,IdNo,WeiXinNumber,PhoneNumber,OtherWork," +
                "EntryDate,PostId,DepartmentId)"+
                "Values('Kiter50','男','北京','123456789123456789','qwer1','96325451784','没有','2024-11-07',10,12)";

            sql += ";select @@Identity";
            SqlCommand cmd = new SqlCommand (sql,conn);
            conn.Open();
            int result = Convert.ToInt32(cmd.ExecuteScalar());
            conn.Close();
            Console.WriteLine("编号:"+result);
        }

        //读取多条记录 (查询的 多个表)
        public void GetReaderList()
        {
            //创建建立连接的对象 -- SqlConnection
            SqlConnection conn = new SqlConnection(connString1);
            //要执行的 sql 语句
            string sql = "select EmployeeName,Gender,NowAddress from Employee";
            //实例化 要执行 sql的对象 -- SqlCommand
            SqlCommand cmd = new SqlCommand(sql, conn);
            //建立连接
            conn.Open();
            //执行结果集查询
            SqlDataReader reader = cmd.ExecuteReader();
            //逐行读取
            while (reader.Read())
            {
                string result = reader["EmployeeName"].ToString() + reader["Gender"] + reader["NowAddress"];
                Console.WriteLine(result);
            }
            //释放资源
            reader.Close(); //关闭读取器
            conn.Close();   //关闭连接
        }

        //读取多条记录(查询的是多个表)
        public void GetReaderList2()
        {
            //创建建立连接的对象 -- SqlConnection
            SqlConnection conn = new SqlConnection(connString1);
            //要执行的 sql 语句
            string sql = "select EmployeeName,Gender,NowAddress from Employee";
            sql += ";select DepartmentId,DepartmentName from Department";
            //实例化 要执行 sql的对象 -- SqlCommand
            SqlCommand cmd = new SqlCommand(sql, conn);
            //建立连接
            conn.Open();
            //执行结果集查询
            SqlDataReader reader = cmd.ExecuteReader();
            //逐行读取
            while (reader.Read())
            {
                string result = reader["EmployeeName"].ToString()+"\t"+reader[1]+"\t"+reader["NowAddress"];
                Console.WriteLine(result);
            }
            Console.WriteLine("*************");
            if (reader.NextResult())
            {
                while (reader.Read())
                {
                    Console.WriteLine($"{reader["DepartmentId"]}\t{reader["DepartmentName"]}");
                }
            }
            //关闭读取器
            reader.Close();
            //关闭连接
            conn.Close();
        }

        //使用 DataSet 和 SqlDataAdapter 读取多条记录
        public void GetDataSet1()
        {
            //创建连接对象
            SqlConnection conn = new SqlConnection(connString1);
            //sql 语句
            string sql = "select EmployeeName,Gender,NowAddress from Employee";
            //创建执行sql的对象
            SqlCommand cmd = new SqlCommand(sql, conn);
            //打开连接
            conn.Open();
            //创建数据适配器对象
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            //创建一个数据集对象
            DataSet ds = new DataSet();
            //将查询到到结果填入到,内存中(DataSet)
            da.Fill(ds);
            //关闭连接
            conn.Close();
            //读取数据
            DataTable dt = ds.Tables[0];
            foreach(DataRow dr in dt.Rows)
            {
                Console.WriteLine($"{dr["EmployeeName"]}\t{dr["Gender"]}\t{dr["NowAddress"]}");
            }
        }

        //使用 DataSet 和 SqlDataAdapter 读取多条记录(查询的是多个表)
        public void GetDataSet2()
        {
            //创建连接对象
            SqlConnection conn = new SqlConnection(connString1);
            //sql 语句
            string sql = "select EmployeeName,Gender,NowAddress from Employee";
            //创建执行sql的对象
            SqlCommand cmd = new SqlCommand(sql, conn);
            //打开连接
            conn.Open();

            //创建数据适配器对象
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            //创建一个数据集对象
            DataSet ds = new DataSet();
            //填充数据
            da.Fill(ds,"Employee");
            cmd.CommandText = "select DepartmentId,DepartmentName from Department";
            da.Fill(ds, "Department");

            //关闭连接
            conn.Close();

            //读取数据
            DataTable dt = ds.Tables["Employee"];
            foreach(DataRow dr in dt.Rows)
            {
                Console.WriteLine($"{dr["EmployeeName"]}\t{dr["Gender"]}\t{dr["NowAddress"]}");
            }
            Console.WriteLine("........................");
            foreach(DataRow dr in ds.Tables["Department"].Rows)
            {
                Console.WriteLine($"{dr["DepartmentId"]}\t{dr["DepartmentName"]}");
            }
        }

        //写带 参数的SQL 语句
        public void GetReaderList5()
        {
            //创建建立连接的对象 -- SqlConnection
            SqlConnection conn = new SqlConnection(connString1);
            //要执行的 sql 语句
            string sql = "select EmployeeName,Gender,NowAddress from Employee where EmployId > @Number";
            SqlParameter[] param = new SqlParameter[]
            {
                new SqlParameter("@Number",106)
            };
            //实例化 要执行 sql的对象 -- SqlCommand
            SqlCommand cmd = new SqlCommand(sql, conn);
            //添加参数
            cmd.Parameters.AddRange(param);
            //建立连接
            conn.Open();
            //执行结果集查询
            SqlDataReader reader = cmd.ExecuteReader();
            //逐行读取
            while (reader.Read())
            {
                string result = reader["EmployeeName"].ToString() + "\t" + reader[1] + "\t" + reader["NowAddress"];
                Console.WriteLine(result);
            }

            //关闭读取器
            reader.Close();
            //关闭连接
            conn.Close();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            SqlServer sqlServer = new SqlServer();
            //建立连接,然后断开
            //sqlServer.ConnectDB();

            //插入新的行
            //sqlServer.Insert();

            //修改数据库中的信息
            //sqlServer.Update();

            //删除数据库中的记录
            //sqlServer.Delete();

            //执行只返回一个结果的 sql 语句
            //sqlServer.GetSingleResult();

            //执行只返回一个结果的 sql 语句
            //sqlServer.GetSingleResult2();

            //用 ExecuteScalar 来执行 插入操作,返回看新增的记录是第几条的
            //sqlServer.GetSingleResult3();

            //读取多条记录
            //sqlServer.GetReaderList();

            //读取多个表的多条记录
            //sqlServer.GetReaderList2();

            //使用 SqlDataAdapter 和 DataSet 读取数据
            //sqlServer.GetDataSet1();

            //使用 SqlDataAdapter 和 DataSet 读取多个表的数据
            //sqlServer.GetDataSet2();

            //使用带参的sql语句
            sqlServer.GetReaderList5();


            Console.ReadLine();
        }
    }
}

查询

在这里插入图片描述
通过关闭 SqlDataReader,来关闭 SqlConnection
在这里插入图片描述

七、SqlHelper

先安装库:
在这里插入图片描述

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
using System.Web;
//using System.Data.SqlClient;
using System.Data;
using Microsoft.SqlServer.Server;

using Microsoft.Data.SqlClient;
using System.Configuration;

namespace ToolsLib
{
    public class SqlServerHelper
    {
        //用于连接数据库的字符串
        //private static string ConnString { get; set; } = ConfigurationManager.ConnectionStrings["connString1"].ToString();
        private static string ConnString { get; set; } = ConfigurationManager.AppSettings["connString1"];

        /// <summary>
        /// 执行 insert\update\delete 类型的 sql 语句
        /// </summary>
        /// <param name="cmdText">sql语句或存储过程名称</param>
        /// <param name="paramArray">参数数组</param>
        /// <returns>受影响的行数</returns>
        /// <exception cref="Exception"></exception>
        public static int ExecuteNonQuery(string cmdText, SqlParameter[] paramArray = null)
        {
            SqlConnection conn = new SqlConnection(ConnString);
            SqlCommand cmd = new SqlCommand(cmdText, conn);
            if (paramArray != null)
            {
                cmd.Parameters.AddRange(paramArray);
            }
            try
            {
                conn.Open();
                return cmd.ExecuteNonQuery();//执行
            }
            catch (Exception ex)
            {
                //可以在这个地方写入日志(log文件)
                string errorMsg = $"{DateTime.Now}:执行public static int ExecuteNonQuery(sting cmdText,SqlParameter[]para---{ex.Message}";
                throw new Exception(errorMsg);
            }
            finally
            {
                conn.Close();
            }
        }
        /// <summary>
        /// 执行查询语句,查询结果是但是一个结果
        /// </summary>
        /// <param name="cmdText"></param>
        /// <param name="paramArray"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static object ExecuteScalar(string cmdText, SqlParameter[] paramArray = null)
        {
            SqlConnection conn = new SqlConnection(ConnString);
            SqlCommand cmd = new SqlCommand(cmdText, conn);
            if (paramArray != null)
            {
                cmd.Parameters.AddRange(paramArray);
            }
            try
            {
                conn.Open();
                return cmd.ExecuteScalar();
            }
            catch (Exception ex)
            {
                throw new Exception("执行public staticobjectExecute Scalar(string cmdText,SqlParameter[] paramArray = null)异常" + ex.Message);
            }
            finally
            {
                conn.Close();//关闭连接
            }
        }
        /// <summary>
        /// 执行查询语句
        /// </summary>
        /// <param name="cmdText"></param>
        /// <param name="paramArray"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static SqlDataReader ExecuteReader(string cmdText, SqlParameter[] paramArray = null)
        {
            SqlConnection conn = new SqlConnection(ConnString);
            SqlCommand cmd = new SqlCommand(cmdText, conn);
            if (paramArray != null)
            {
                cmd.Parameters.AddRange(paramArray);
            }
            try
            {
                conn.Open();
                //这里返回的 SqlDataReader 是用来进行进一步查询的,
                //这里的 加的入参是:CommandBehavior.CloseConnection  为了,关闭 SqlDataReader后来自动关闭 conn连接 做设置
                //因为 SqlDataReader 是需要在外部进行访问的
                return cmd.ExecuteReader(CommandBehavior.CloseConnection);//执行
            }
            catch (Exception ex)
            {
                throw new Exception($"执行public staticobjectExecute Scalar(stringcmdText,SqlParameter[] paramArray=null) ---{ex.Message}");
            }
        }
        /// <summary>
        /// 返回包含一张数据表的数据集的查询
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="tableName"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static DataSet GetDataSet(string sql, string tableName = null)
        {
            SqlConnection conn = new SqlConnection(ConnString);
            SqlCommand cmd = new SqlCommand(sql, conn);
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            DataSet ds = new DataSet();

            try
            {
                conn.Open();
                if (tableName == null)
                {
                    da.Fill(ds);
                }
                else
                {
                    da.Fill(ds, tableName);
                }
                return ds;
            }
            catch (Exception ex)
            {
                throw new Exception($"执行public static DataSet GetDataSet(string sql,string tableName=null)方法出现异常{ex.Message}");
            }
            finally
            {
                conn.Close();
            }
        }

        public static DataSet GetDataSet(Dictionary<string, string> dicTableAndSql)
        {
            SqlConnection conn = new SqlConnection(ConnString);
            SqlCommand cmd = new SqlCommand();
            cmd.Connection = conn;
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            DataSet ds = new DataSet();
            try
            {
                conn.Open();
                foreach (string tbName in dicTableAndSql.Keys)
                {
                    cmd.CommandText = dicTableAndSql[tbName];
                    da.Fill(ds, tbName);//加入多个表
                }
                return ds;
            }
            catch (Exception ex)
            {
                throw new Exception("执行public static DataSet GetDataSet(string ssql,string tableName=null)方法出行异常" + ex.Message);
            }
            finally
            {
                conn.Close();
            }
        }
    }
}

八、存储过程的写法:

创建

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE TestProcedure
	@Parameter1 Int =0,
	@Parameter2 Int output
AS
BEGIN
    SET NOCOUNT ON
	select * from UserT;

	Set @Parameter2 = 999;
END
GO

修改

USE [MyDB]
GO
/****** Object:  StoredProcedure [dbo].[TestProcedure]    Script Date: 12/17/2024 10:08:29 AM ******/
-- 与null比较的结果会被视为 未知,而不是 true 或 false
SET ANSI_NULLS ON
GO
-- 可以使用用双引号,引起来的关键字
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[TestProcedure]
    -- 输入参数
	@Parameter1 Int =0,
	-- 输出参数
	@Parameter2 Int output
AS
BEGIN
    -- 为了不返回 每条sql 影响多少条记录的信息
    SET NOCOUNT ON
	select * from UserT;

	Set @Parameter2 = 999;
END

执行

declare @Parameter2 int;

exec TestProcedure @Parameter1=20, @Parameter2= @Parameter2 output;

select @Parameter2 as Parameter2;

3、NModbus4 通讯库的使用

1、使用串口,封装 NModbus4 库

安装 NModbus4 库:
在这里插入图片描述

封装的代码:

using Modbus.Device;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WindowsFormsApp1
{
    /// <summary>
    /// 基于NModbus4的开源库的二次封装
    /// </summary>
    internal class ModbusRTU
    {
        #region 串口打开
        //声明.NET串口对象
        private SerialPort serialPort;
        //声明Modbus协议串口主设备对象
        private ModbusSerialMaster master;
        //                         COM1          9600               N           8              1             
        public bool Connect(string portName, int baudRate,Parity parity,int dataBits,StopBits stopBits)
        {
            if(this.serialPort == null && this.serialPort.IsOpen)
            {
                this.serialPort.Close();
            }
            try
            {
                //创建.NET串口对象
                this.serialPort = new SerialPort(portName,baudRate, parity,dataBits,stopBits);
                //设置串口的读写超时时间(防止长时间阻塞)
                this.serialPort.ReadTimeout = 1000;
                this.serialPort.WriteTimeout = 1000;

                //打开 .NET 串口
                this.serialPort.Open();
                //使用 Modbus串口工厂方法 创建 Modbus串口主设备 对象
                master = ModbusSerialMaster.CreateRtu(this.serialPort);

                return true;
            }
            catch(Exception ex)
            {
                //打印异常信息
                throw new Exception("[串口]打开失败,"+ex.Message);
            }
        }
        #endregion

        #region 关闭串口
        public void DisConnect()
        {
            if(this.serialPort != null && this.serialPort.IsOpen)
            {
                //this.serialPort?.Close();
                this.serialPort.Close();
            }
            master = null;
        }
        #endregion

        #region 读取数据
        /// <summary>
        /// 【01】功能码:读取输出线圈
        /// </summary>
        /// <param name="slaveId">从站地址</param>
        /// <param name="start">起始线圈地址</param>
        /// <param name="length">线圈的数量</param>
        /// <returns>返回bool数组</returns>
        /// <exception cref="Exception"></exception>
        public bool[]ReadOutputCoils(byte slaveId,ushort start,ushort length)
        {
            try
            {
                //Coils 线圈的意思
                return this.master.ReadCoils(slaveId, start, length);
            }
            catch(Exception ex)
            {
                throw new Exception("[读取输出线程]失败" + ex.Message);
            }
        }

        /// <summary>
        /// [02] 功能码:读取输入线圈
        /// </summary>
        /// <param name="slaveId">从站地址</param>
        /// <param name="start">起始线圈地址</param>
        /// <param name="length">线圈的数量</param>
        /// <returns>返回bool数组</returns>
        /// <exception cref="Exception"></exception>
        public bool[] ReadInputCoils(byte slaveId, ushort start, ushort length)
        {
            try
            {
                return this.master.ReadInputs(slaveId, start, length);
            }
            catch (Exception ex)
            {
                throw new Exception("[读取输入线圈]失败:" + ex.Message);
            }
        }

        // 一个寄存器是两个 字节的大小
        /// <summary>
        /// 【03】 功能码:读取输出寄存器
        /// </summary>
        /// <param name="slaveId">从站地址</param>
        /// <param name="start">起始寄存器地址</param>
        /// <param name="length">寄存器的数量</param>
        /// <returns>返回byte数组</returns>
        /// <exception cref="Exception"></exception>
        public byte[] ReadHoldingRegister(byte slaveId,ushort start,ushort length)
        {
            try
            {
                //获取数据数组
                ushort[] data = this.master.ReadHoldingRegisters(slaveId, start, length);
                // 一个寄存器是两个 字节的大小
                //把ushort类型数组,转换成List字节数组
                List<byte> result = new List<byte>();
                foreach(var item in data)
                {
                    result.AddRange(BitConverter.GetBytes(item));
                }
                return result.ToArray();
            }
            catch(Exception ex)
            {
                throw new Exception("[读取输出寄存器]失败," + ex.Message);
            }
        }
        /// <summary>
        /// [04] 功能码:读取输入寄存器
        /// </summary>
        /// <param name="slaveId">从站地址</param>
        /// <param name="start">起始寄存器地址</param>
        /// <param name="length">寄存器的数量</param>
        /// <returns>返回byte数组</returns>
        /// <exception cref="Exception"></exception>
        public byte[] ReadInputRegister(byte slaveId,ushort start,ushort length)
        {
            try
            {
                //获取数据数组
                ushort[] data = this.master.ReadInputRegisters(slaveId, start, length);
                //把ushort类型的数组,转换成List字节数组
                List<byte> result = new List<byte>();
                foreach (var item in data)
                {
                    result.AddRange(BitConverter.GetBytes(item));
                }
                return result.ToArray();
            }
            catch(Exception ex)
            {
                throw new Exception("[读取输入寄存器]失败" + ex.Message);
            }
        }
        
        #endregion

        #region 写入数据
        /// <summary>
        /// [05] 功能码:预置单线圈
        /// </summary>
        /// <param name="slaveId">从站地址</param>
        /// <param name="start">当前线圈地址</param>
        /// <param name="value">线圈的值</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public bool PreSetSingleCoil(byte slaveId,ushort start,bool value)
        {
            try
            {
                this.master.WriteSingleCoil(slaveId, start, value);
                return true;
            }
            catch(Exception ex)
            {
                throw new Exception("[预置单线圈]失败," + ex.Message);
            }
        }
        /// <summary>
        /// [06]功能码:预置单寄存器
        /// </summary>
        /// <param name="slaveId">从站地址</param>
        /// <param name="address">寄存器地址</param>
        /// <param name="value">字节地址(2个字节)</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public bool PreSetSingleRegister(byte slaveId,ushort address,byte[] value)
        {
            try
            {
                this.master.WriteSingleRegister(slaveId, address, BitConverter.ToUInt16(value, 0));
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception("【预置单寄存器】失败," + ex.Message);
            }
        }

        public bool PreSetSingleRegister(byte slaveId,ushort address,short value)
        {
            return PreSetSingleRegister(slaveId, address, BitConverter.GetBytes(value));
        }
        public bool PreSetSingleRegister(byte slaveId,ushort address,ushort value)
        {
            return PreSetSingleRegister(slaveId, address, BitConverter.GetBytes(value));
        }
        /// <summary>
        /// 【0F】 功能码 预置多个线圈
        /// </summary>
        /// <param name="slaveId">从站地址</param>
        /// <param name="start">线圈开始地址</param>
        /// <param name="value">布尔数组</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public bool PreSetMutiCoils(byte slaveId,ushort start,bool[] value)
        {
            try
            {
                this.master.WriteMultipleCoils(slaveId, start, value);
                return true;
            }
            catch(Exception ex){
                throw new Exception("[预制多线圈]失败," + ex.Message);
            }
        }
        /// <summary>
        /// [10] 功能码:预制多个寄存器
        /// </summary>
        /// <param name="slaveId">从站地址</param>
        /// <param name="start">寄存器开始地址</param>
        /// <param name="values">字节数组</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public bool PreSetMultiRegister(byte slaveId,ushort start, byte[] values)
        {
            //必须是偶数字节
            // 因为两字节 , 才是也给寄存器的大小
            if(values == null||values.Length == 0 || values.Length%2 == 1)
            {
                return false;
            }
            //将字节数组转换成ushort数组
            ushort[] data = new ushort[values.Length / 2];
            for (int i = 0; i < values.Length; i += 2)
            {
                data[i] = BitConverter.ToUInt16(values, i);
            }
            try
            {
                this.master.WriteMultipleRegisters(slaveId, start, data);
                return true;
            }
            catch(Exception ex)
            {
                throw new Exception("[预制多寄存器]失败,"+ex.Message);
            }
        }
        /// <summary>
        /// [0F] 功能码:预制多个线圈
        /// </summary>
        /// <param name="slaveId">站地址</param>
        /// <param name="start">线圈开始地址</param>
        /// <param name="value">布尔数组</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public bool PreSetMultiCoils(byte slaveId,ushort start,bool[] value)
        {
            try
            {
                this.master.WriteMultipleCoils(slaveId,start,value);
                return true;
            }
            catch (Exception ex) 
            {
                throw new Exception("[预制多个线圈]失败" + ex.Message);
            }
        }
        #endregion
    }
}

三、手写通信库

四、WPF基本使用

0、xaml 的基础操作

xaml是一种声明型语言,一般来讲,一个标签就是一个对象;而一个标签的属性就是一个对象的属性。
给标签属性赋值有三种方式:

1、 Attribute = Value 形式

画一个 长方形

        <Rectangle Width="100" Height="80" Stroke="Black"/>

画一个三角形

        <Path Data="M 0,0 L 200,100 L 100,200 Z" Stroke="Black" Fill="Red"/>
将一个字符串转换成标签(对象)的写法:

在这里插入图片描述
MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
        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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <local:Dog x:Key="dog1" Name="Bob1"/>
        <local:Dog x:Key="dog2" Name="Bob2"/>
        <local:Dog x:Key="dog3"  Name="Bob3" Child="123"/>
    </Window.Resources>
    <Grid>
        <Button Content="Hello!" Width="120" Height="30" Click="Button_Click"/>
    </Grid>
</Window>

Dog.cs

using System.ComponentModel;
using System.Globalization;

namespace WpfApp1
{
	//为类添加转换规则
    [TypeConverterAttribute(typeof(NameToDogTypeConverter))]
    public class Dog
    {
        public string Name { get; set; }
        public Dog Child { get; set; }
    }

    public class NameToDogTypeConverter : TypeConverter
    {
    	//将字符串转成 Dog 的规则
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            string name = value.ToString();
            Dog child = new Dog();
            child.Name = name;
            return child;
        }
    }
}

MainWindow.xaml.cs

using System.Windows;

namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Dog dog =this.FindResource("dog3") as Dog; ;//找到字典资源中 标签对象的方法
            if(null != dog)
            {
            	//取出标签对象中的属性
                MessageBox.Show(dog.Name + "/" + dog.Child.Name);
            }
        }
    }
}

另一种等价的添加属性 的方式:

        <Button Content="登录" FontSize="20" Height="50" Width="300"/>
        <Button Content="登录">
            <Setter Property="Background" Value="Red"/>
            <Setter Property="FontSize" Value="20"/>
            <Setter Property="Height" Value="50"/>
            <Setter Property="Width" Value="300"/>
		</Button>
2、属性标签

形如:
<LinearGradientBrush.StartPoint>
就是属性标签,它不是一个对象,而是对象的属性,用标签的形式来写
例子 1:
在这里插入图片描述

<Window x:Class="WpfApp1.MainWindow"
        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"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Rectangle Width="200" Height="160" Stroke="Blue">
            <Rectangle.Fill>
                <LinearGradientBrush>
                    <LinearGradientBrush.StartPoint>
                        <Point X="0" Y="0"/>
                    </LinearGradientBrush.StartPoint>
                    <LinearGradientBrush.EndPoint>
                        <Point X="1" Y="1"/>
                    </LinearGradientBrush.EndPoint>
                    <LinearGradientBrush.GradientStops>
                        <GradientStopCollection>
                            <GradientStop Offset="0.2" Color="LightBlue"/>
                            <GradientStop Offset="0.7" Color="DarkBlue"/>
                            <GradientStop Offset="1.0" Color="Blue"/>
                        </GradientStopCollection>
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
    </Grid>
</Window>

例子 2:
在这里插入图片描述

<Window x:Class="WpfApp1.MainWindow"
        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"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Width="120" Height="30">
            <Button.Content>
                <Rectangle Width="20" Height="20" Stroke="DarkGreen" Fill="LawnGreen"/>
            </Button.Content>
        </Button>
    </Grid>
</Window>

3、标签扩展

在这里插入图片描述

1、创建一个项目

在这里插入图片描述

程序入口:
在这里插入图片描述

默认入口点:WPF 应用程序的默认入口点是 App.xaml 和 App.xaml.cs 文件。在这些文件中定义了应用程序的启动逻辑和主窗口。
自定义入口点:如果需要,可以在代码中定义一个 Main 方法并在其中创建和运行 Application 对象,但这不是必需的,除非你有特定的初始化需求。

手写函数函数入口(一般不需要):

// Entry point defined in a custom Main method (if needed)
public static class Program
{
    [STAThread]
    public static void Main()
    {
        var app = new App();
        app.InitializeComponent();
        app.Run();
    }
}
// App.xaml.cs
using System.Windows;

namespace MyWpfApp
{
    public partial class App : Application
    {
        // Application startup logic can be placed here
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            // Custom startup logic (if needed)
        }

        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);
            // Custom exit logic (if needed)
        }
    }
}

窗体 xaml 文件的解读:

在这里插入图片描述

在这里插入图片描述

2、模拟一个文本编辑的界面(使用控件:Grid | StackPanel | Button | TextBox)

准备

button的属性: Width HorizontalAlignment VerticalAlignment Height
在这里插入图片描述

    <Grid>
        <Button Width="200" HorizontalAlignment="Left" VerticalAlignment="Top" Height="40"/>
        <Button Width="200" HorizontalAlignment="Center" VerticalAlignment="Top" Height="40"/>
        <Button Width="200" HorizontalAlignment="Right" VerticalAlignment="Top" Height="40"/>

        <Button Width="200" HorizontalAlignment="Left" VerticalAlignment="Center" Height="40"/>
        <Button Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" Height="40"/>
        <Button Width="200" HorizontalAlignment="Right" VerticalAlignment="Center" Height="40"/>

        <Button Width="200" HorizontalAlignment="Left" VerticalAlignment="Bottom" Height="40"/>
        <Button Width="200" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="40"/>
        <Button Width="200" HorizontalAlignment="Right" VerticalAlignment="Bottom" Height="40"/>
    </Grid>

Stackanel控件:
.在这里插入图片描述

占用多列的写法:
Grid.ColumnSpan=“2”

        <StackPanel Orientation="Vertical" HorizontalAlignment="Center">
            <Button Height="20" Width="70"/>
            <Button Height="20" Width="70"/>
            <Button Height="20" Width="70"/>
        </StackPanel>

在这里插入图片描述

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
            <Button Height="20" Width="70"/>
            <Button Height="20" Width="70"/>
            <Button Height="20" Width="70"/>
        </StackPanel>

Grid控件:
在这里插入图片描述

    <Grid ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
        </Grid.ColumnDefinitions>

        <Button Grid.Row="1" Grid.Column="1">1,1</Button>
        <Button Grid.Row="1" Grid.Column="2">1,2</Button>
        <Button Grid.Row="1" Grid.Column="3">1,3</Button>
        <Button Grid.Row="2" Grid.Column="1">2,1</Button>
        <Button Grid.Row="2" Grid.Column="2">2,2</Button>
        <Button Grid.Row="2" Grid.Column="3">2,3</Button>
    </Grid>

Grid 的三种长度设置:
AUTO 安内容来
绝对宽高 每个单位是 1/96英寸
“1*” 按比例来

TextBox 文本编辑的控件

在这里插入图片描述

        <TextBox TextWrapping="Wrap"/>

应用

在这里插入图片描述

<Window x:Class="WpfApp1.EditWindow"
        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:WpfApp1"
        mc:Ignorable="d"
        Title="EditWindow" Height="450" Width="800">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
            <RowDefinition Height="20"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal">
            <Button Height="20" Width="70" Content="文件"/>
            <Button Height="20" Width="70" Content="编辑"/>
            <Button Height="20" Width="70" Content="查看"/>
            <Button Height="20" Width="70" Content="外观"/>
            <Button Height="20" Width="70" Content="设置"/>
        </StackPanel>
        <StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal">
            <Button Height="20" Width="20" Content="1"/>
            <Button Height="20" Width="20" Content="2"/>
            <Button Height="20" Width="20" Content="3"/>
            <Button Height="20" Width="20" Content="4"/>
            <Button Height="20" Width="20" Content="5"/>
        </StackPanel>
        <Grid Grid.Row="2" Grid.Column="0">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="40"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <StackPanel Grid.Column="0" Grid.Row="0">
                    <Button Height="20" Content="1"/>
                    <Button Height="20" Content="2"/>
                    <Button Height="20" Content="3"/>
                    <Button Height="20" Content="4"/>
                    <Button Height="20" Content="5"/>
                    <Button Height="20" Content="6"/>
                    <Button Height="20" Content="7"/>
                    <Button Height="20" Content="8"/>
                    <Button Height="20" Content="9"/>
                    <Button Height="20" Content="10"/>
                    <Button Height="20" Content="11"/>
                    <Button Height="20" Content="12"/>
                    <Button Height="20" Content="13"/>
                    <Button Height="20" Content="14"/>
                    <Button Height="20" Content="15"/>
                    <Button Height="20" Content="16"/>
                    <Button Height="20" Content="17"/>
                </StackPanel>
                <TextBox Grid.Column="1" TextWrapping="Wrap"/>

            </Grid>
        </Grid>
        <Grid Grid.Row="3" Grid.Column="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto"/>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>
            <Button Grid.Column="0">Normal text file</Button>
            <Button Grid.Column="1">Length:1,125</Button>
            <Button Grid.Column="2">lines:26</Button>
            <Button Grid.Column="3">Ln:6 Col:57 Sel:3</Button>
            <Button Grid.Column="4">1</Button>
            <Button Grid.Column="5">Windows(CR LF)</Button>
            <Button Grid.Column="6">UTF-8-BOM</Button>
            <Button Grid.Column="7">INS</Button>
        </Grid>
    </Grid>
</Window>

2-2 布局器的使用:

1、StackPanel 水平或垂直排列元素、Orientation 属性分别为:Horizontal / Verical

在这里插入图片描述

在这里插入图片描述

2、WrapPanel 水平或垂直排列元素、剩余控件不足会进行换行、换列的排布

在这里插入图片描述
在这里插入图片描述

3、DockPanel 根据容器的边界、元素进行 Dock.Top 、Left 、Right 、Bottom

在这里插入图片描述

4、Grid 类似 table表格

在这里插入图片描述

5、UniformGrid 指定行和列的数量,均匀有限的容器空间

在这里插入图片描述

6、Canvas 使用固定的坐标设置元素的位置

在这里插入图片描述

3、样式

在这里插入图片描述

样式写在:

< Window.Resources > 里的 < Style > 里 //定义
在标签里加属性Style: Style=“{StaticResource LoginStyle}” //使用

StaticResource 静态加载
DynamicResource 动态加载,在运行的时候,改变 xaml 文件内容,样式是会发生改变的

	<Window x:Class="WpfApp1.EditWindow"
	        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:WpfApp1"
	        mc:Ignorable="d"
	        Title="EditWindow" Height="450" Width="800">
	    <Window.Resources>
	        <Style TargetType="Button">
	            <Setter Property="Background" Value="WhiteSmoke"/>
	            <Setter Property="FontSize" Value="20"/>
	            <Setter Property="Height" Value="50"/>
	            <Setter Property="Width" Value="300"/>
	            <Setter Property="Margin" Value="20,10"/>
	        </Style>
	        <Style x:Key="LoginStyle"  TargetType="Button">
	            <Setter Property="Background" Value="Green"/>
	            <Setter Property="FontSize" Value="20"/>
	            <Setter Property="Height" Value="50"/>
	            <Setter Property="Width" Value="300"/>
	        </Style>
	        <Style x:Key="QuitStyle"  TargetType="Button" BasedOn="{StaticResource {x:Type Button} }">
	            <Setter Property="Background" Value="Red"/>
	        </Style>
	    </Window.Resources>
	    <StackPanel>
	        <Button Style="{StaticResource LoginStyle}" Content="登录"/>
	        <Button Style="{DynamicResource QuitStyle}" Content="退出"/>
	        <Button Content="忘记密码"/>
	    </StackPanel>
	</Window>

继承:

在这里插入图片描述

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="登录界面" Height="270" Width="500" ResizeMode="NoResize">
    <Window.Resources>
        <Style x:Key="baseButtonStyle" TargetType="Button">
            <Setter Property="FontSize" Value="30"/>
            <Setter Property="Foreground" Value="Blue"/>
        </Style>
        <Style x:Key="defaultButtonStyle" TargetType="Button" BasedOn="{StaticResource baseButtonStyle}">
            <Setter Property="Width" Value="100"/>
            <Setter Property="Height" Value="50"/>
        </Style>
    </Window.Resources>
    <Grid>
        <Button Style="{StaticResource defaultButtonStyle}" Content="ghyu"/>
    </Grid>
</Window>

4、添加资源字典

第一步:添加资源字典 xaml 文件

在这里插入图片描述
资源字典文件:Dictionary1.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="Button">
        <Setter Property="Background" Value="WhiteSmoke"/>
        <Setter Property="FontSize" Value="20"/>
        <Setter Property="Height" Value="50"/>
        <Setter Property="Width" Value="300"/>
        <Setter Property="Margin" Value="20,10"/>
    </Style>
    <Style x:Key="LoginStyle"  TargetType="Button">
        <Setter Property="Background" Value="Green"/>
        <Setter Property="FontSize" Value="20"/>
        <Setter Property="Height" Value="50"/>
        <Setter Property="Width" Value="300"/>
    </Style>
    <Style x:Key="QuitStyle"  TargetType="Button" BasedOn="{StaticResource {x:Type Button} }">
        <Setter Property="Background" Value="Red"/>
    </Style>
</ResourceDictionary>

第二步:在 app.xml 文件中引入 资源字典文件

            <ResourceDictionary Source="/WpfApp1;component/Dictionary1.xaml"/>
            这里的  WpfApp1  是  命名空间
            Dictionary1.xaml  是  要加载的文件名
<Application x:Class="WpfApp1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp1"
             StartupUri="EditWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/WpfApp1;component/Dictionary1.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

第三步:在标签中,可以直接调用

在这里插入图片描述

<Window x:Class="WpfApp1.EditWindow"
        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:WpfApp1"
        mc:Ignorable="d"
        Title="EditWindow" Height="450" Width="800">
    <StackPanel>
        <Button Style="{StaticResource LoginStyle}" Content="登录"/>
        <Button Style="{DynamicResource QuitStyle}" Content="退出"/>
        <Button Content="忘记密码"/>
    </StackPanel>
</Window>

5、用模板自定义一个带圆角的 Button 控件 及 触发器 的写法

在这里插入图片描述

            <ControlTemplate TargetType="Button">

里 TargetType=“Button” 和 TargetTye=“{x:Type Button}” 是一样的

                <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="6">

在这一行中,{TemplateBinding Background}" 表示从原 button 标签中去取 叫 Background 的属性

<Window x:Class="WpfApp1.MainWindow"
        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:WpfApp1"
        mc:Ignorable="d"
        Title="123" Height="450" Width="800">
    <Grid>
        <Button Content="btn" Background="Red" BorderBrush="Black" FontSize="20" Width="200" Height="30" BorderThickness="3">
            <Button.Template>
                <ControlTemplate TargetType="Button">
                    <Border x:Name="boder" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="6">
                        <TextBlock Text="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="boder" Property="Background" Value="Black"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter TargetName="boder" Property="Background" Value="WhiteSmoke"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Button.Template>
        </Button>
    </Grid>
</Window>


解读:

Grid: 一个布局容器,用于布局子元素。在这个例子中,它包含了一个 Button 控件。

Button: 一个按钮控件,具有以下属性:
Content=“btn”: 按钮的显示文本为 “btn”。
Background=“Red”: 按钮的背景颜色为红色。
BorderBrush=“Black”: 按钮的边框颜色为黑色。
FontSize=“20”: 按钮文本的字体大小为 20。
Width=“200”: 按钮的宽度为 200 像素。
Height=“30”: 按钮的高度为 30 像素。
BorderThickness=“3”: 按钮的边框厚度为 3 像素。

ControlTemplate: 定义了 Button 控件的外观模板。TargetType=“Button” 指定这个模板用于 Button 控件。
Border: 包含了按钮的主要视觉部分。
x:Name=“boder”: 给 Border 起了一个名字 boder,以便在触发器中引用。
Background=“{TemplateBinding Background}”: Border 的背景颜色绑定到按钮的 Background 属性。
BorderBrush=“{TemplateBinding BorderBrush}”: Border 的边框颜色绑定到按钮的 BorderBrush 属性。
BorderThickness=“{TemplateBinding BorderThickness}”: Border 的边框厚度绑定到按钮的 BorderThickness 属性。
CornerRadius=“6”: Border 的圆角半径设置为 6 像素,使边角有一定的圆润效果。
TextBlock: 显示按钮的文本内容。
Text=“{TemplateBinding Content}”: TextBlock 的文本绑定到按钮的 Content 属性。
HorizontalAlignment=“Center”: 文本在水平方向居中对齐。
VerticalAlignment=“Center”: 文本在垂直方向居中对齐.
在这里插入图片描述
在这里插入图片描述

5-2、触发器 的另一些实践

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="登录界面" Height="270" Width="500">
    <Window.Resources>
        <Style x:Key="defaultButtonStyle" TargetType="Button">
            <Setter Property="Width" Value="100"/>
            <Setter Property="Height" Value="30"/>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Foreground" Value="Red"/>
                    <Setter Property="FontSize" Value="30"/>
                </Trigger>
                <Trigger Property="IsMouseOver" Value="False">
                    <Setter Property="Foreground" Value="Blue"/>
                    <Setter Property="FontSize" Value="20"/>
                </Trigger>
            </Style.Triggers>
        </Style>

        <Style x:Key="defaultButtonStyle2" TargetType="Button">
            <Setter Property="Width" Value="100"/>
            <Setter Property="Height" Value="30"/>
            <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsMouseOver" Value="true"/>
                        <Condition Property="IsFocused" Value="True"/>
                    </MultiTrigger.Conditions>
                    <MultiTrigger.Setters>
                        <Setter Property="Foreground" Value="Red"/>
                    </MultiTrigger.Setters>
                </MultiTrigger>
            </Style.Triggers>
        </Style>

        <Style x:Key="defaultButtonStyle3" TargetType="Button">
            <Setter Property="Width" Value="100"/>
            <Setter Property="Height" Value="30"/>
            <Style.Triggers>
                <EventTrigger RoutedEvent="Mouse.MouseEnter">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Duration="0:0:0.2"
                                                 Storyboard.TargetProperty="FontSize"
                                                 To="30">
                                    
                                </DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <StackPanel>
        <Button Style="{StaticResource defaultButtonStyle}" Content="Hello"/>
        <Button Style="{StaticResource defaultButtonStyle2}" Content="Hello"/>
        <Button Style="{StaticResource defaultButtonStyle3}" Content="Hello"/>
    </StackPanel>
</Window>

5-3、生成模板副本:

在这里插入图片描述

在这里插入图片描述
将 模板放在 资源字典中:
在这里插入图片描述

5-4、控件模板

在这里插入图片描述

5-5、数据模板

第一个例子:
在这里插入图片描述
在这里插入图片描述
第二个例子:
在这里插入图片描述
在这里插入图片描述

6、 button 的 和 点击事件 的写法:

在这里插入图片描述

6-2、添加点击事件的两种方式:

1 直接在 xaml 代码中进行添加
2 根据名字找到控件的 点击事件,在 cs 代码中添加
在这里插入图片描述

<Window x:Class="WpfApp1.MainWindow"
        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:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <sys:String x:Key="stringHello">Hello WPF!</sys:String>
    </Window.Resources>
    <Grid>
        <TextBlock Height="24" Width="120" Background="LightBlue"
                   Text="{StaticResource ResourceKey=stringHello}"/>
    </Grid>
</Window>

7-1、控件间的属性绑定

在这里插入图片描述

    <Grid>
        <StackPanel>
            <Slider x:Name="slider" Margin="5"/>
            <TextBox
                Height="30"
                Margin="5"
                Text="{Binding ElementName=slider, Path=Value, Mode=OneTime}"/>
            <!--只进行一次绑定-->
            <TextBox
                Height="30"
                Margin="5"
                
                Text="{Binding ElementName=slider, Path=Value, Mode=OneWay}"/>
            <!--单向绑定-->
            <TextBox
                Height="30"
                Margin="5"
                Text="{Binding ElementName=slider, Path=Value}"/>
            <!--默认是双向绑定-->
        </StackPanel>
    </Grid>

7-2、一个简单的数据绑定的写法(属性的变更通知)

完成前 3 步,可以实现 数据从界面 向 代码的传递
完成后 2 步,可以实现 界面 向 代码层的数据传递
在这里插入图片描述

在这里插入图片描述

代码:

在这里插入图片描述

<Window x:Class="WpfApp1.MainWindow"
        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:WpfApp1"
        mc:Ignorable="d"
        Title="登录界面" Height="270" Width="500" ResizeMode="NoResize">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="15"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="5*"/>
        </Grid.RowDefinitions>
        <TextBox Grid.Row="1" Text="X6337TEB6----登录系统" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
        <Grid Grid.Row="2">
            <Grid.RowDefinitions>
                <RowDefinition Height="20"/>
                <RowDefinition Height="20"/>
                <RowDefinition Height="20"/>
                <RowDefinition Height="27"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="auto"/>
                <ColumnDefinition Width="150"/>
                <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="1" Text="用户名"/>
            <TextBox Text ="{Binding UserName}" Grid.Row="0" Grid.Column="2" Margin="3,2"/>
            <TextBlock Grid.Row="1" Grid.Column="1" Text="密码"/>
            <TextBox Text="{Binding PassWord}" Grid.Row="1" Grid.Column="2" Margin="3,2"/>
            <CheckBox Grid.ColumnSpan="2"  Grid.Row="2" Grid.Column="1" Content="记住密码"/>
            <Button Grid.ColumnSpan="2" Grid.Row="3" Grid.Column="1" Content="登录" Margin="3,1" Click="Button_Click"/>
        </Grid>
    </Grid>
</Window>

using System;
using System.ComponentModel;
using System.Windows;

namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window,INotifyPropertyChanged
    {
        #region 数据绑定的固定写法
        private string _userName;
        private string _passWord;
        public string UserName {
            get { return _userName; }
            set 
            { 
                _userName = value;
                RaisePropertyChanged("UserName");
            } 
        }
        public string PassWord 
        {
            get { return _passWord; } 
            set 
            {
                _passWord = value;
                RaisePropertyChanged("PassWord");
            } 
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;
        }

        /// <summary>
        /// 登录按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Console.WriteLine($"{UserName}-{PassWord}");
            UserName = "Admin";
            PassWord = "123";
        }
    }
}

8、MVVM(与 7 是同一个界面)

MVVM是为里前后端的分离
MVVM与MVC,VM 是对 C 的升级(依靠的是 双向的数据属性 和 单向的命令属性)
V 的修改 不会影响到 其他部分代码的编译

MVVM 和 MVC 的区别
MVVM
M Model
V View
VM ViewModel
在这里插入图片描述

MVC
M Model
V View
C Control
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8-1.1 带参的方法的写法:

传入 Tag

<Button Grid.Row="0" Command="{Binding ClickBtn}" Tag="a" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Tag}">a</Button>
public ICommand ClickBtn
{
    get
    {
        return new ExecuteCommond((param) =>
        {
            // param 是 CommandParameter 传递的值
            string tag = param as string;
            Console.WriteLine($"Tag: {tag}");
        });
    }
}

传入控件自身

<Button Grid.Row="0" Command="{Binding ClickBtn}" Tag="a" CommandParameter="{Binding RelativeSource={RelativeSource Self}}">a</Button>
public ICommand ClickBtn
{
    get
    {
        return new ExecuteCommond((param) =>
        {
            if (param is Button button)
            {
                var tag = button.Tag; // 获取按钮的Tag属性
                Console.WriteLine($"Tag: {tag}");
            }
        });
    }
}

多种入参的 ICommand 的实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace QIPWaterDeal.ViewModel
{
    public class ExecuteCommond : ICommand
    {
        /// <summary>
        /// 判断命令是否可以执行
        /// </summary>
        private readonly Func<bool> _canExecute;

        /// <summary>
        /// 执行无参数的操作
        /// </summary>
        private readonly Action _execute;

        /// <summary>
        /// 执行带参数的操作
        /// </summary>
        private readonly Action<object> _executeWithParameter;

        /// <summary>
        /// 构造方法(无参数版本)
        /// </summary>
        public ExecuteCommond(Action execute, Func<bool> canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        /// <summary>
        /// 构造方法(带参数版本)
        /// </summary>
        public ExecuteCommond(Action<object> executeWithParameter, Func<bool> canExecute = null)
        {
            _executeWithParameter = executeWithParameter;
            _canExecute = canExecute;
        }

        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// 是否可以执行命令
        /// </summary>
        public bool CanExecute(object parameter)
        {
            return _canExecute == null || _canExecute();
        }

        /// <summary>
        /// 执行命令
        /// </summary>
        public void Execute(object parameter)
        {
            if (_execute != null)
            {
                _execute.Invoke();
            }
            else if (_executeWithParameter != null)
            {
                _executeWithParameter.Invoke(parameter);
            }
        }

        /// <summary>
        /// 通知CanExecute状态发生变化
        /// </summary>
        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    }
}

8-2 MVVM的另一种实践(对 进行包装)

在这里插入图片描述

一个实际的例子
在这里插入图片描述
在这里插入图片描述

MainWindow.xml

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="登录界面" Height="270" Width="500" ResizeMode="NoResize">
    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBox x:Name="input1" Width="100" Height="24" Margin="3" Text="{Binding Input1}"></TextBox>
            <TextBox x:Name="input2" Width="100" Height="24" Margin="3" Text="{Binding Input2}"></TextBox>
            <TextBox x:Name="input3" Width="100" Height="24" Margin="3" Text="{Binding Input3}"></TextBox>
            <Button x:Name="btn1" Width="100" Height="24" Margin="3" Content="Add" Command="{Binding AddCommand}"></Button>
        </StackPanel>
    </Grid>
</Window>

NotificationObject

using System.ComponentModel;

namespace WpfApp1
{
    /// <summary>
    /// VM 的基类
    /// </summary>
    public class NotificationObject:INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChange(string propertyName)
        {
            if(this.PropertyChanged != null)
            {
                this.PropertyChanged.Invoke(this,new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

DelegateCommand

using System;
using System.Windows.Input;

namespace WpfApp1
{
    public class DelegateCommand:ICommand
    {
        public bool CanExecute(object parameter)
        {
            if(this.CanExecuteFunc == null)
            {
                return true;
            }

            return this.CanExecuteFunc(parameter);
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            if(this.ExecuteAction == null)
            {
                return;
            }
            this.ExecuteAction(parameter);
        }
        public Action<object> ExecuteAction { get; set; }
        public Func<object,bool> CanExecuteFunc { get; set; }
    }
}

MainWindowViewModel

using System;

namespace WpfApp1
{
    internal class MainWindowViewModel : NotificationObject
    {
        #region 数据属性
        private double input1;
        public double Input1 
        {
            get
            { 
                return input1;
            }
            set 
            { 
                input1 = value;
                this.RaisePropertyChange(nameof(Input1));
            }
        }
        private double input2;
        public double Input2 
        {
            get
            {
                return input2;
            }
            set
            {
                input2 = value;
                this.RaisePropertyChange(nameof(Input2));
            }
        }
        private double input3;
        public double Input3 {
            get {
                return input3;
            }
            set {
                input3 = value;
                this.RaisePropertyChange(nameof(Input3));
            }
        }
        #endregion

        #region 命令属性
        public DelegateCommand AddCommand { get; set; }
        private void Add(object parameter)
        {
            this.Input3 = this.Input1 + this.Input2;
        }
        public MainWindowViewModel()
        {
            this.AddCommand = new DelegateCommand();
            this.AddCommand.ExecuteAction = new Action<object>(this.Add);
        }
        #endregion
    }
}

8-3、利用 特性(反射),优化数据变更通知(接口)的写法

在这里插入图片描述

9、写一个自定义控件(添加 自定义 依赖属性)

在这里插入图片描述
字典资源
在这里插入图片描述
加入字典资源
在这里插入图片描述

继承 Button 的自定义控件
在这里插入图片描述
使用:
在这里插入图片描述

10、导入程序集和引用其中的名称空间:

在这里插入图片描述

然后选 带 Framework 的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

<UserControl x:Class="WpfControlLibrary3.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfControlLibrary3"
             mc:Ignorable="d" 
             d:DesignHeight="160" d:DesignWidth="240">
    <Grid>
        <Canvas>
            <Label Canvas.Left="12" Canvas.Top="12" Content="第一部分" Height="28" Name="label1"/>
            <Label Canvas.Left="12" Canvas.Top="46" Content="第二部分" Height="28" Name="label2"/>
            <Label Canvas.Left="12" Canvas.Top="80" Content="第三部分" Height="28" Name="label3"/>
            <TextBox Canvas.Left="88" Canvas.Top="14" Height="23" Name="textBox1" Width="140"/>
            <TextBox Canvas.Left="88" Canvas.Top="48" Height="23" Name="textBox2" Width="140"/>
            <TextBox Canvas.Left="88" Canvas.Top="82" Height="23" Name="textBox3" Width="140"/>

            <Button Canvas.Left="88" Canvas.Top="125" Content="计算" Height="23" Name="button1" Width="140" Click="button_Click"/>
        </Canvas>
    </Grid>
</UserControl>

添加引用:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

11、一些 x 命名空间的使用

x:Class
在这里插入图片描述

x:ClassModifier
在这里插入图片描述
x:Name
在这里插入图片描述
x:FieldModifier
在这里插入图片描述

12、在WPF中加载 Winform 的 Form

1、在wpf 中添加引用

System.Windows.Forms.Integration

System.Windows.Forms.Integration
注:System.Windows.Forms.Integration 在 Net Formwork 4.7.2 中叫 WindowsFormsIntegration
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2、创建用户控件

在wpf 项目中创建 winform 控件

using System.Windows.Forms;
using WindowsFormsControlLibrary1;

namespace WpfApp1
{
    public partial class UserControl1 : UserControl
    {
        private Form1 _form1;
        public UserControl1()
        {
            InitializeComponent();
            _form1 = new Form1();
            _form1.TopLevel = false;
            _form1.Dock = DockStyle.Fill;
            this.Controls.Add(_form1);
            _form1.Show();
        }
    }
}

在 主界面中 WindowsFormsHost 加入标签,在代码中加载 Winform 的控件,借助Winform控件 加载 winform 窗体

    <Grid>
        <WindowsFormsHost Name="windowsFormsHost" />
    </Grid>   
using System.Windows;
namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            UserControl1 userControl1 = new UserControl1();

            windowsFormsHost.Child = userControl1;
        }
    }
}

13、动画

动画有三种:
线性动画:DouleAnmim
关键帧动画:DoubleAnimationUsingkeyFrams
路径动画:DoubleAnimationUsingPath
在这里插入图片描述

    <Grid>
        <StackPanel>
            <Button x:Name="btn" Width="100" Height="24" Content="带动画的按钮" Click="Button_Click"/>
            <Button x:Name="btn2" Width="100" Height="24" Content="带动画的按钮" Click="Button_Click2"/>
            <Button x:Name="btn3" Width="100" Height="24" Content="带动画的按钮" Click="Button_Click3"/>
        </StackPanel>
    </Grid>
#define C
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //创建一个双精度的动画
            DoubleAnimation animation = new DoubleAnimation();
            animation.From = btn.Width;//设置动画的初始值
            animation.To = btn.Width - 30;//设置动画的结束值
            animation.Duration = TimeSpan.FromSeconds(2);//设置动画的持续时间

            //在当前按钮上实行该动画
            btn.BeginAnimation(Button.WidthProperty,
                               animation);
        }

        private void Button_Click2(object sender, RoutedEventArgs e)
        {
            //创建一个双精度的动画
            DoubleAnimation animation = new DoubleAnimation();
            animation.From = btn2.Width;//设置动画的初始值
            animation.To = btn2.Width - 30;//设置动画的结束值
            animation.Duration = TimeSpan.FromSeconds(2);//设置动画的持续时间
            animation.AutoReverse = true;  //是否往返执行
            animation.RepeatBehavior = RepeatBehavior.Forever; //执行周期

            //在当前按钮上实行该动画
            btn2.BeginAnimation(Button.WidthProperty,
                               animation);
        }

        private void Button_Click3(object sender, RoutedEventArgs e)
        {
            //创建一个双精度的动画
            DoubleAnimation animation = new DoubleAnimation();
            animation.From = btn3.Width;//设置动画的初始值
            animation.To = btn3.Width - 30;//设置动画的结束值
            animation.Duration = TimeSpan.FromSeconds(2);//设置动画的持续时间
            animation.AutoReverse = true;  //是否往返执行
            animation.RepeatBehavior = new RepeatBehavior(5);//重复5次
            animation.Completed += Animation_Completed;//动画结束的回调

            //在当前按钮上实行该动画
            btn3.BeginAnimation(Button.WidthProperty,
                               animation);
        }

        private void Animation_Completed(object sender,EventArgs e)
        {
            btn3.Content = "动画已完成";
        }
    }


}

14、WPF 和 Prism

其他

1、获取当前文件目录

string currentDirectory = AppDomain.CurrentDomain.BaseDirectory;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/971968.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu24安装MongoDB(解压版)

目录 0.需求说明1.环境检查2.下载软件2.1.下载MongoDB服务端2.2.下载MongoDB连接工具(可略过)2.3.检查上传或下载的安装包 3.安装MongoDB3.1.编辑系统服务3.2.启动服务3.3.客户端连接验证3.3.1.创建管理员用户 4.远程访问4.1.开启远程访问4.2.开放防火墙 0.需求说明 问&#x…

《DeepSeek-V3:人工智能大语言模型》

《DeepSeek-V3:人工智能大语言模型》 1. 引言 我们介绍了 DeepSeek-V3,这是一个强大的专家混合 (MoE) 语言模型,总共有 671B 个参数,每个令牌激活了 37B。 为了实现高效的推理和具有成本效益的训练,DeepSeek-V3 采用了多头潜在注意力 (MLA) 和 DeepSeekMoE 架构,这些…

解锁机器学习核心算法 | K -近邻算法:机器学习的神奇钥匙

一、引言 今天我们继续学习机器学习核心算法 —— K - 近邻&#xff08;K-Nearest Neighbors&#xff0c;简称 KNN&#xff09;算法。它就像是一位经验丰富的 “老江湖”&#xff0c;以其简单而又强大的方式&#xff0c;在众多机器学习任务中占据着不可或缺的地位。 K - 近邻…

算法分析—— 《归并排序》

《排序数组》 题目描述&#xff1a; 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 你必须在 不使用任何内置函数 的情况下解决问题&#xff0c;时间复杂度为 O(nlog(n))&#xff0c;并且空间复杂度尽可能小。 示例 1&#xff1a; 输入&#xff1a;nums [5,2…

linux云服务器部署deepseek,并通过网页访问

参考视频&#xff1a;https://www.douyin.com/root/search/linux%E5%AE%89%E8%A3%85%20deepseek?aid3aa2527c-e4f2-4059-b724-ab81a140fa8b&modal_id7468518885570940214&typegeneral 修改ollama配置文件 vim /etc/systemd/system/ollama.service 我的电脑硬盘只有4…

FastAdmin后端列表导入表格数据

后台添加数据的时候增加通过表格导入功能 如下图index.html页面增加导入和模板下载按钮代码如下 <div class"panel panel-default panel-intro">{:build_heading()}<div class"panel-body"><div id"myTabContent" class"ta…

可调节图片参数,解决图片模糊及尺寸过小问题的工具

软件介绍 你是否正为图片模糊、尺寸太小而烦恼&#xff1f;别担心&#xff0c;有这样一款神器能帮你轻松解决。它能精准调节图片参数&#xff0c;即便原本模糊不清的图片&#xff0c;经它处理后也能变得高清锐利&#xff0c;瞬间让图片焕然一新。而且&#xff0c;它还具备导出…

Windows网络安全基础

随着互联网的发展和普及&#xff0c;Windows网络安全问题愈发严重。在本文中&#xff0c;我们将会介绍Windows网络安全的基本概念&#xff0c;包括网络攻击类型、网络安全威胁、网络安全防御措施等等&#xff0c;帮助初学者更好地了解Windows网络安全。 一、网络攻击类型 网络…

代码补全『三重奏』:EverEdit如何用上下文识别+语法感知+智能片段重构你的编码效率!

1 代码自动完成 1.1 应用场景 在编辑文档时&#xff0c;为了提高编辑效率&#xff0c;编辑器一般都会带有自动完成功能&#xff0c;比如&#xff1a;输入括号时自动补全另一半&#xff0c;输入文字时&#xff0c;自动补全剩下的部分。 1.2 使用方法 1.2.1 自动缩进 单击主菜…

vue,vue3 keepalive没有效果,无法缓存页面include无效,keep-alive

keepalive没有效果&#xff0c;无法缓存页面&#xff1f; 问题大概是组件的name值不对应&#xff0c;vue2修改组件文件的name值&#xff0c;vue3保持组件文件名称和路由页面配置的name一致就可以了&#xff0c;如果vue3不想保持一致&#xff0c;必须手动在文件后面添加export..…

栈回溯方案

注&#xff1a;栈回溯无法很好的定位到未调优化的函数&#xff0c;需要编译前使用 -fno-optimize-sibling-calls 选项禁止尾调优化。 基于unwind的栈回溯 在 arm 架构下&#xff0c;不少32位系统用的是 unwind 形式的栈回溯&#xff0c;这种栈回溯要复杂很多。首先需要程序有一…

【存储中间件API】MySQL、Redis、MongoDB、ES常见api操作及性能比较

常见中间件api操作及性能比较 ☝️ MySQL crud操作✌️ maven依赖✌️ 配置✌️ 定义实体类✌️ 常用api ☝️ Redis crud操作✌️ maven依赖✌️ 配置✌️ 常用api ☝️ MongoDB crud操作✌️ maven依赖✌️ 配置文件✌️ 定义实体类✌️ MongoDB常用api ☝️ ES crud操作 ⭐️…

解锁D3.js与PlantUML的交互奥秘:探索知识图谱数据可视化新领域

解锁D3.js与PlantUML的交互魔法&#xff1a;数据可视化新征程 在前端开发的广袤天地里&#xff0c;数据可视化一直是一颗璀璨的明珠&#xff0c;吸引着无数开发者探索其奥秘。而当D3.js这一强大的JavaScript库&#xff0c;遇上专注于创建UML图的PlantUML&#xff0c;一场奇妙的…

DeepSeek24小时写作机器人,持续创作高质量文案

内容创作已成为企业、自媒体和创作者的核心竞争力。面对海量的内容需求&#xff0c;人工创作效率低、成本高、质量参差不齐等问题日益凸显。如何在有限时间内产出高质量内容&#xff1f;DeepSeek写作机器人&#xff0c;一款24小时持续创作的智能工具&#xff0c;为企业和个人提…

使用html css js 来实现一个服装行业的企业站源码-静态网站模板

最近在练习 前端基础&#xff0c;html css 和js 为了加强 代码的 熟悉程序&#xff0c;就使用 前端 写了一个个服装行业的企业站。把使用的技术 和 页面效果分享给大家。 应用场景 该制衣服装工厂官网前端静态网站模板主要用于前端练习和编程练习&#xff0c;适合初学者进行 HT…

使用html css js 开发一个 教育机构前端静态网站模板

这个教育机构网站模板是专为前端开发初学者设计的练习项目&#xff0c;适合正在学习前端的学生或自学者使用。网站内容包括首页、课程体系、师资力量、关于我们和联系我们等基础页面&#xff0c;帮助学习者熟悉网页布局、样式设计和交互功能的实现。 静态页面 简单截图 应用…

(蓝桥杯——10. 小郑做志愿者)洛斯里克城志愿者问题详解

题目背景 小郑是一名大学生,她决定通过做志愿者来增加自己的综合分。她的任务是帮助游客解决交通困难的问题。洛斯里克城是一个六朝古都,拥有 N 个区域和古老的地铁系统。地铁线路覆盖了树形结构上的某些路径,游客会询问两个区域是否可以通过某条地铁线路直达,以及有多少条…

React 低代码项目:网络请求与问卷基础实现

&#x1f35e;吐司问卷&#xff1a;网络请求与问卷基础实现 Date: February 10, 2025 Log 技术要点&#xff1a; HTTP协议XMLHttpRequest、fetch、axiosmock.js、postmanWebpack devServer 代理、craco.js 扩展 webpackRestful API 开发要点&#xff1a; 搭建 mock 服务 …

大流量汽(柴)油机泵,抗洪抢险的可靠选择|深圳鼎跃

近年来&#xff0c;全球范围内极端天气频发&#xff0c;洪涝灾害成为危及人民生命财产安全的重要因素。在抗洪抢险行动中&#xff0c;如何迅速、高效地排除积水&#xff0c;保障救援通道和安全区域成为关键。汽柴油机泵凭借其动力强、移动灵活、环境适应性强等特点&#xff0c;…

游戏开发微信小程序--工具箱之父

小程序工具箱之父已更新 Page({data: {score: 0,lives: 3,gameOver: false,playerVisible: true,level: 1,petType: cat,speedBuff: 1,coins: 0,friends: [],achievements: [],currentPetFrame: 0, // 当前宠物动画帧scoreMultiplier: 1, // 得分倍率gameSpeed: 1, // …