快速入门:【c# 之 Winform开发】

C#基础

面向对象(OOP)

c语言是面向过程。

c++是面向过程+面向对象。

c#是纯粹的面向对象: 核心思想是以人的思维习惯来分析和解决问题。万物皆对象

面向对象开发步骤:

  1. 分析对象

    • 特征
    • 行为
    • 关系(对象关系/类关系)
  2. 写代码:

    特征–>成员变量

    方法–>成员方法

  3. 实例化–具体对象

Note:

  1. 类=模板

  2. (类我们一般用于定义新的数据类型)

    (定义好的类 = 新的数据类型,故可以用于定义对应类型变量)

  3. 类的成员分为普通成员和静态成员

  4. 类间关系:

    • 泛化(Generalization):

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gn2rVTSc-1691668028037)(winform.assets/image-20230725163347635.png)]

  • 实现(Realization):

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bhq5RcJZ-1691668028039)(winform.assets/image-20230725163408766.png)]

  • 关联(Association):

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uTxa81U5-1691668028039)(winform.assets/image-20230725163635397.png)]

  • 聚合(Aggregation):

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HDM44oGQ-1691668028040)(winform.assets/image-20230725163654287.png)]

  • 组合(Composition):

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1zUakHTU-1691668028041)(winform.assets/image-20230725163718282.png)]

  • 依赖(Dependency):

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vtzgU5tQ-1691668028042)(winform.assets/image-20230725163800924.png)]

.Net(framework)和C#

两者的关系:

.Net Framework 的地位类似于 Java中的JVM.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T4TM7qvF-1691668028043)(.\winform.assets\image-20230721142942294.png)]

c#语言的编译过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kX3BeY4j-1691668028044)(.\winform.assets\image-20230721143002777.png)]

Note:

  1. C#的语言编译器: csc(c sharp compiler),其与.Net框架安装在一起,即在同一个安装路径下。

    例如,在我本机的路径是:C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe

数据类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-knf7hSxl-1691668028045)(.\winform.assets\image-20230721145931912.png)]

属性

c#中,类中的属性和方法,若没有定义public等权限修饰符时,默认是private,只能在类内进行访问。

若要跨类访问,可以修改为public的权限修饰符。如: public float size;但直接修改为public属性的缺点是外部的类可以任意修改一个类的属性值,所以遇到这种情况,在Java中的解决方案是外部类利用类内的getter和setter方法去访问和修改类的成员变量,同时类内的getter和setter方法也可在方法中进行一定的逻辑处理和限制,从而达到不让外部类随便改的目的。

而在C#中,则是利用访问器/属性解决这个问题,其思想也类似于Java中的getter和setter。

常规属性

  1. 什么是常规属性?

答:

  • 在类中,为了安全起见,一般把数据成员设置为private。为了外部访问和修改的方便,一般创建一个首字母大小写不同的同名public属性,属性中添加get(获取值)和set(修改值)方法。

  • 先定义一个私有的字段,然后在为这个私有字段封装一个公开的属性,在属性中实现 get 和 set 两个方法,这种方式叫做常规属性。当我们使用常规属性的时候,可以在 get 和 set 方法中,编写逻辑代码对取值和赋值进行逻辑的校验。

  • C#中常规属性用于对类中成员变量的取值或赋值进行限制过滤。

如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1I3qRXRs-1691668028045)(winform.assets/image-20230725094357581.png)]

上图中,我们将size这个字段的权限修饰符由public改为private,同时定义了一个属性代码public float Size{},其中 的get{}代码段用于返回属性值,其中的set{}代码段用于设置值,且其中的value是保留字,其代表了外界赋给这个字段的值

如上:

private权限的变量命名一般首字母小写。

而public权限的变量命令一般首字母大写。

自动属性

  1. 什么是自动属性?

答:其是 微软提供的一种建立属性的方式。

在某些情况下,属性的 get 和 set 只是完成字段的取值和赋值操作,而不包含任何附加的逻辑代码,这个时候可以使用自动属性。如:public int Age { get; set; },其相当于get{}代码段里面默认写好了return某个字段,set{}代码段里面默认写好了xx字段=value。

总的来说,是一种相比常规属性而言更加简洁的写法。

在vs中输入prop ,点击两次tab会生成一个默认的自动属性

(一般在vs中输入一个词之后双击tab会自动填充完整,有时再结合按下enter键)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AC2wit33-1691668028046)(winform.assets/image-20230725103134580.png)]

我们使用自动属性的时候,就不需要再写对应的字段了,C#编译器会自动给我们的自动属性提供一个对应的字段,即不用手工声明一个私有的字段。

  1. 自动属性注意的问题:
  • 要同时实现get和set访问器,不能使用自动属性只实现其中的一种,如public String Name { get; }这种写法是会报错的。

  • 如果是只能只读/只能只写,自动属性代码如何写?如下:

    加个private从而达到限制的目的。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xwzf3fwG-1691668028046)(winform.assets/image-20230725101707393.png)]

  1. 对比常规属性自动属性:

    • 自动属性写起来的更快,且不用自己写get,set方法,直接使用默认的;

    • 将自动属性的set去掉,则在类外无法修改属性的值,只可以获取属性的值;

    • 两者来说,可能自动属性对比常规属性,可能会少些两句代码,但不如常规属性自己写的方法灵活,按需使用。

继承与多态

  1. 多态的理解: 同一类/类型的多个实例对同一个消息做出不同的反应。

  2. C#中的继承仅支持单一继承。

    继承的格式: 当前类名:继承的基类名

  3. C#中若不想一个类被继承,可以添加sealed关键字。

  1. C#中所有类型都继承自System.Object(隐式继承,即没有明显的写出xx类 : System.Object的形式).

  2. c#中的base关键字类似于Java中的super关键字。

  3. c#中的new关键字作用:

    要么可以在实例化一个对象时使用;

    要么可以隐藏基类(父类)成员时使用:


如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KMVvo6Vu-1691668028052)(winform.assets/image-20230725150721366.png)]

(当基类已存在Print()方法时,将子类的同名Print()方法加上一个new关键字即可隐藏基类成员,即这个Print()方法只属于这个子类,即子类的这个同名方法和基类同名方法是独立的, 同时也不会报一个绿色波浪线错误)

  1. 子类重写基类方法时,会涉及到virtualoverride关键字。其中基类的方法会添加virtual关键字,而子类的同名方法会添加override关键字。

    • virtual修饰符:virtual修饰符可以将一个方法声明为虚拟方法。

      即声明当前的方法只是一个占位符,具体的操作都得靠其派生类来实现。

      虚方法的作用就是在子类继承父类以后,可以去重写父类中的方法,而在子类中,重写父类的虚方法时,方法的访问权限,返回类型,方法名,以及参数的类型与数量必须一样,而可以重写的只是方法的主体部分中的代码。

    • override修饰符:

  1. 关于用基类变量声明子类实例对象,在调用基类和子类中的同名方法时的调用结果问题:

    • case1: 当没碰到重写时,不管子类中的同名方法上有没有带有new关键字,用实例对象.同名方法调用的最终都是基类中的同名方法。
    • case2: 当碰到重写时,即基类同名方法带有virtual关键字,子类同名方法带有override关键字时,此时用实例对象.同名方法调用的最终都是子类中同名方法

抽象类与接口

抽象类:

  • 方法用abstract关键字修饰,则类也要用abstract关键字修饰。

接口:

  • 接口可以实现多个。

  • 接口名常以大写I开头,如:IMyInterface

  • 接口的实现格式: 类名:接口名

    由于继承和接口实现都是用:的格式,

    当碰到某个类既要继承某个类,还有实现某个接口时,注意此时继承类写在前面,实现的接口写在后面,中间以,隔开:

    如: class Manager:Employee,IMyInterface

    因为一个类继承的类只能有一个,但实现的接口可以有多个。

抽象类和接口的使用选择:

  • 当涉及到继承/复用时,考虑抽象类,否则优先考虑接口。

匿名函数

在C#中,匿名函数是一种无需命名的函数,可以在代码中定义和使用。匿名函数通常用作委托的参数,以便在运行时将其传递给其他函数或方法。

C#中有两种类型的匿名函数:lambda表达式和匿名方法。

匿名方法

匿名方法是一种使用delegate关键字定义的无需命名的方法。

它的语法如下:

delegate (parameter_list) { statements }

其中,parameter_list是一个用逗号分隔的参数列表,statements是一系列语句。

例如,以下代码创建了一个匿名方法,将两个整数相加并返回结果:

Func<int, int, int> add = delegate(int x, int y) { return x + y; };//泛型委托
int result = add(3, 4); // result = 7

lambda表达式

Lambda表达式是一种匿名函数,它可以快速地创建一个委托或表达式树。

它的语法如下:

(parameter_list) => expression

其中,parameter_list是一个用逗号分隔的参数列表,expression是一个返回值的表达式。

例如,以下代码创建了一个lambda表达式,将两个整数相加并返回结果:

Func<int, int, int> add = (x, y) => x + y;//泛型委托
int result = add(3, 4); // result = 7

委托

在C#中,委托是一种引用方法的类型,它允许您将方法作为参数传递给其他方法将方法存储在变量中。

委托可以被认为是函数指针,但具有更高级别的安全性和功能。

委托定义了一个方法签名,它指定了可以由该委托引用的方法的返回类型和参数列表

可以将任何符合该方法签名的方法分配给该委托,使其成为它的实例。一旦将方法分配给委托,您可以像调用方法一样调用该委托,以便在调用委托时执行该方法。

下面的实例演示了委托的声明、实例化和使用,该委托可用于引用带有一个整型参数的方法,并返回一个整型值。

demo.cs

using System;
//委托的声明
delegate int NumberChanger(int n);

namespace DelegateAppl
{
   class TestDelegate
   {
      static int num = 10;
      public static int AddNum(int p)
      {
         num += p;
         return num;
      }

      public static int MultNum(int q)
      {
         num *= q;
         return num;
      }
      public static int getNum()
      {
         return num;
      }

      static void Main(string[] args)
      {
         // 创建委托实例(委托的实例化)
         NumberChanger nc1 = new NumberChanger(AddNum);
         NumberChanger nc2 = new NumberChanger(MultNum);
         // 使用委托对象调用方法(委托的使用)
         nc1(25);
         Console.WriteLine("Value of Num: {0}", getNum());
         nc2(5);
         Console.WriteLine("Value of Num: {0}", getNum());
         Console.ReadKey();
      }
   }
}

普通委托

在C#中,定义普通委托的语法格式如下:

delegate ();

//如果委托的定义符合一定的格式规范,可以省略 delegate 关键字。

其中,<returnType>是委托所返回的类型;<delegateName>是委托的名称;<parameters>是委托所接收的参数列表。

例如,定义一个名为MyDelegate的委托,它接收两个整数参数并返回一个整数类型的值,可以使用以下代码:

delegate int MyDelegate(int x, int y);

以下是一个简单的委托定义和应用的示例:

//定义一个普通委托
delegate int Calculate(int x, int y);


//test
class Program
{
    static int Add(int x, int y)
    {
        return x + y;
    }

    static void Main(string[] args)
    {
        Calculate calc = Add;
        int result = calc(3, 4); // result = 7
    }
}

//在上面的代码中,我们定义了一个委托Calculate,它接受两个整数参数并返回一个整数。我们还定义了一个静态方法Add,它符合Calculate委托的方法签名。我们使用Add方法来实例化Calculate委托,并在调用calc委托时执行Add方法,调用委托执行对应的方法。

泛型委托

使用泛型委托可以为方法的参数和返回类型提供更大的灵活性和重用性,尤其是在编写通用代码时非常有用。

C#中定义泛型委托的语法:

delegate ();

//如果委托的定义符合一定的格式规范,可以省略 delegate 关键字

其中,<return type> 表示委托所表示方法的返回类型,<delegate name> 是委托的名称,<T> 表示泛型类型参数,<T parameters> 是泛型方法的参数列表。

以下是一个示例,展示如何定义一个简单的泛型委托类型:

delegate T MyGenericDelegate<T>(T x);
//这个泛型委托类型的名称是 MyGenericDelegate,它表示一个方法,该方法接受一个泛型类型的参数 T 并返回一个相同类型的值。

下面是一个使用泛型委托的示例:

// 假设有一个泛型委托,用于处理任意类型的数据
delegate void ProcessDataDelegate<T>(T data);

// 假设有一个类用于处理整数数据
class IntegerProcessor
{
    public static void ProcessInteger(int number)
    {
        Console.WriteLine("Processing integer: " + number);
    }
}

// 假设有一个类用于处理字符串数据
class StringProcessor
{
    public static void ProcessString(string text)
    {
        Console.WriteLine("Processing string: " + text);
    }
}

class Program
{
    static void Main()
    {
        // 创建泛型委托实例,指向整数处理方法
        ProcessDataDelegate<int> intDelegate = IntegerProcessor.ProcessInteger;

        // 创建泛型委托实例,指向字符串处理方法
        ProcessDataDelegate<string> stringDelegate = StringProcessor.ProcessString;

        // 使用泛型委托处理整数数据
        intDelegate(10);

        // 使用泛型委托处理字符串数据
        stringDelegate("Hello, world!");
    }
}

//在上面的示例中,首先定义了一个泛型委托 ProcessDataDelegate<T>,它可以处理任意类型的数据。
//然后,通过创建委托实例并将其指向不同的处理方法,分别处理整数和字符串数据。
//最后,通过调用委托实例,将数据传递给相应的处理方法进行处理。

C# 事件

事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。

在C#中,事件是一种特殊的委托,用于实现发布-订阅模式。通过事件,一个对象可以通知其他对象发生了特定的动作或状态改变,而不需要显式地了解或引用其他对象。

C# 中使用事件机制实现线程间的通信。

定义格式:

定义一个事件需要使用event关键字,其后是一个委托类型(可以是通用委托类型,也可以是自定义的委托类型)。通常,事件的名称以On开头。

例如,下面是一个名为OnButtonClick的事件:

public event EventHandler OnButtonClick;
//此处,EventHandler是.NET Framework中的一个通用委托类型,它接受两个参数:一个是事件源(即发送事件的对象),另一个是事件参数。

通过事件使用委托

下面是一个简单的示例,演示如何在C#中使用事件和委托:

这个示例展示了C#中通过事件和委托实现发布-订阅模式的基本用法。

using System;

// 定义事件发布者类
public class Publisher
{
    // 声明一个事件,使用EventHandler委托
    public event EventHandler<CustomEventArgs> CustomEvent;

    // 触发事件的方法
    public void TriggerEvent(string message)
    {
        // 创建事件参数对象
        var args = new CustomEventArgs(message);

        // 检查事件是否有订阅者,并触发事件
        CustomEvent?.Invoke(this, args);
    }
}

// 定义事件参数类
public class CustomEventArgs : EventArgs
{
    public string Message { get; }

    public CustomEventArgs(string message)
    {
        Message = message;
    }
}

// 定义事件订阅者类
public class Subscriber
{
    // 事件处理方法
    public void HandleEvent(object sender, CustomEventArgs args)
    {
        Console.WriteLine("订阅者收到消息:" + args.Message);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        // 创建发布者对象和订阅者对象
        var publisher = new Publisher();
        var subscriber = new Subscriber();

        // 订阅事件(事件绑定事件处理方法)
        publisher.CustomEvent += subscriber.HandleEvent;

        // 触发事件(手动触发)
        publisher.TriggerEvent("Hello, World!");

        // 取消订阅事件(事件取消绑定事件处理方法)
        publisher.CustomEvent -= subscriber.HandleEvent;

        // 再次触发事件(没有订阅者)
        publisher.TriggerEvent("Event without subscribers");

        Console.ReadLine();
    }
}
/**输出结果是:
订阅者收到消息:Hello, World!

该代码定义了一个事件发布者类 Publisher,其中声明了一个使用 EventHandler 委托的事件 CustomEvent,并定义了一个触发事件的方法 TriggerEvent。同时,该代码还定义了一个事件参数类 CustomEventArgs 和一个事件订阅者类 Subscriber,其中 Subscriber 类中有一个处理事件的方法 HandleEvent。

在 Main 方法中,首先创建了一个 Publisher 对象和一个 Subscriber 对象,并将 Subscriber 对象的 HandleEvent 方法订阅到 Publisher 对象的 CustomEvent 事件上,即 publisher.CustomEvent += subscriber.HandleEvent;。接着,调用 publisher.TriggerEvent("Hello, World!"); 触发事件,Subscriber 对象的 HandleEvent 方法会被调用,输出结果为 "订阅者收到消息:Hello, World!"。之后,取消订阅事件 publisher.CustomEvent -= subscriber.HandleEvent;,再次调用 publisher.TriggerEvent("Event without subscribers"); 触发事件,由于没有订阅者,不会有任何输出结果。最后,通过 Console.ReadLine(); 使程序等待用户输入,以便查看**/
---------------------------------------------------
    
//在上面的示例中,我们首先定义了一个Publisher类,它包含一个名为CustomEvent的事件。该事件使用EventHandler<CustomEventArgs>委托进行声明,委托的类型指定了事件处理方法的签名。

//然后,我们定义了一个CustomEventArgs类作为事件参数,其中包含了一个Message属性。

//接下来,我们定义了一个Subscriber类,它包含一个HandleEvent方法来处理事件。该方法的参数匹配事件委托的签名。

//在Main方法中,我们创建了一个发布者对象publisher和一个订阅者对象subscriber。然后,我们使用+=运算符将订阅者的处理方法添加到事件的订阅列表中。

//最后,我们通过调用publisher.TriggerEvent方法来触发事件,并传递一个消息作为参数。订阅者的处理方法将被调用,并输出收到的消息。

//注意,在示例中我们还演示了如何取消订阅事件,通过使用-=运算符从事件的订阅列表中移除订阅者的处理方法。

C# 注意:

  1. var只能定义在类的成员方法内部,且是一个假的动态类型定义,因为其在编译时即可确定类型,真正的动态类型定义是用dynamic,其在运行时确定类型。

  2. 如下的成对写法是的等效的:

    为什么要有两种写法呢?

    因为其中的小写类型开头的是c#定义的,而大写类型开头的则是.Net framework的CLR里的CTS定义的。这样的话不同类型语言到了.Net框架后才可以达到一个相同的定义。

    所以小写属于c#写法,大写属于.Net框架写法。

  3. c#中的Struct数据类型类似Class类型,但区别是Class是属于引用类型,Struct是属于值类型。

  4. 注意值类型引用类型在内存(栈和堆)中的分配形式:

    c#的值类型是在栈中分配空间,即值存储在栈中。

    c#的引用类型是在堆中分配空间,栈中只保存其引用。

    即栈中保存指向堆的地址,堆中保存真实的对象和数据整体。

    如下:

  5. 实例化一个对象时,从内存角度分析:

    case1:


case2:


6. 方法调用时形参的值传递(默认行为,传递变量的副本):

值类型的形参引用类型的形参在方法调用都是值传递

所谓值传递: 即copy一份副本出来调用。

注意: 跟形参的类型是值类型或者是引用类型都无关。

对于值类型的形参:


对于引用类型的形参(方法体中不带有new一个实例对象)

方法改变的是同一个对象。


对于引用类型的形参(方法体中带有new一个实例对象):

方法改变的是方法中创建的新对象。

  1. 方法调用时形参的引用传递:

    由于默认值传递,要实现引用传递只能做特殊处理。

    • 法一:在形参变量的前面加上ref关键字,同时传入实参时也在实参变量前面加上ref关键字。
    • 法二:在形参变量的前面加上out关键字,同时传入实参时也在实参变量前面加上out关键字。

    这两种特殊处理的区别:

    1. 法二需要在方法体里面添加一句形参变量的初始化语句才不会报错。


      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ERMWueIG-1691668028057)(.\winform.assets\image-20230721170558027.png)]

  2. 对于接收数字的形参变量的类型定义:

    只要不参与运算,方法的形参接收的数字均定义为string类型。

    只有参与运算,形参的数字才定义为数字类型。


  3. 如上所示,小数会默认被认为是double类型,若想标识为float,后面就要加f;同理,若要标识为decimal,后面就要加m。

  4. 在类的成员方法中声明的本地变量,在声明时还要赋值,因为不赋值其是没有默认值的。如: int age = 5.

    但是.若是定义在类中的成员变量,则不需要赋值,其也是有默认值的.如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SiBhl27v-1691668028057)(winform.assets/image-20230725085407638.png)]

  5. this代表实例化以后的当前实例对象。

  6. 类的静态成员(变量或方法)是属于类的,而不是属于实例对象

    在表示时一般用类名.xx进行表示。

    类的静态成员变量在内存中也是存储在堆中。

  7. c#中没有全局变量的说法,即没法在类之外定义变量。

    所以全局变量的思路往往采用类的静态成员的思路解决。

  8. 构造函数:

    • 名字和类名相同。
    • 没有返回值,void也不行。
    • 使用public作为权限修饰符,因为private的话就不能用new来创建这个类的对象了。
    • 没有手动写有参构造方法时,一般编译器会默认生成一个无参构造函数(并赋值默认值),而如果手动写有参构造方法,则编译器不会默认生成一个无参构造函数,需要另外手动写无参构造函数。
  9. c#的完全限定名 = 命名空间 + 类名。

    当打印一个实例对象时,结果就是一个完全限定名。

  10. Object类的ToString()方法:用于将当前对象实例以字符串的形式来表示。

  11. Visual Studio怎么快速多行注释和取消注释?

    用鼠标选中要注释的代码,然后,先按 Ctrl – K 组合键,再按 Ctrl – C 组合键进行注释。

    如果要取消注释,就选中被注释的代码,然后,先按 Ctrl – K 组合键,再按 Ctrl – U 组合键即可取消注释。

  12. C#中的internal关键字限制的访问范围是?

    即同一个项目内部是可以访问的。

    或者同一个dll文件内部是可以访问的。

    因为通常来讲一个项目生成一个dll文件。

  13. C#中的extern关键字?

    在 C# 中,extern 关键字用于声明一个外部方法。它用于指示编译器该方法的实现是在外部的,即在当前的代码文件之外,通常是在其他的本机语言(如 C++)或外部库中实现的。使用 extern 关键字声明的方法不需要提供方法体,因为它的实现在其他地方。这样可以使 C# 代码与其他语言或库进行交互。

  14. C#中的out关键字?

    使用 out 关键字的主要原因是为了允许方法返回多个值。在 C# 中,方法只能返回一个值。但是,如果你需要从方法中返回多个值,你可以将其中一个或多个值作为 out 参数传递给该方法。

    总之,out 关键字的作用是允许方法返回多个值,因为它允许在方法内部修改参数的值,并在方法完成后将修改后的值传递回调用方。

    以下是一个简单的示例,展示了如何使用 out 关键字:

    public void Calculate(int input, out int output)
    {
        // 在这里进行计算
        output = 42; // 将计算结果保存在输出参数中
    }
    
    // 调用 Calculate 方法
    int result;
    Calculate(10, out result);
    Console.WriteLine(result); // 输出 42
    
    //在上面的示例中,Calculate 方法接受一个整数作为输入,并将计算结果保存在 output 参数中。
    //在调用该方法时,我们将一个变量作为 out 参数传递给该方法,以便在方法完成后获取计算结果。
    

    我的理解: 使用out关键字后,传递进去的实参经过方法内部修改后也会更新保存到这个实参中。

  15. 在C#中,?.运算符??运算符是用于处理可能为null的引用类型的特殊运算符.

    1. ?.运算符(Null 条件成员访问运算符):

      ?.运算符允许您在访问引用类型成员之前进行空值检查。

      它的作用是如果左侧的操作数为null,则整个表达式的结果为null,否则(不为Null)才会继续访问成员。

      示例:

      int? length = name?.Length;
      Console.WriteLine(length);  // 输出:null
      

      在上面的示例中,如果name为null,则name?.Length表达式的结果将为null,而不会抛出NullReferenceException。这在避免空引用异常的情况下很有用。

    2. ??运算符(null 合并运算符): ??运算符用于在表达式中处理可能为null的引用类型,并提供一个默认值,当左侧的操作数为null时使用该默认值。

      示例:

      string displayName = name ?? "Guest";
      Console.WriteLine(displayName);  // 输出:Guest
      

      在上面的示例中,如果name为null,则name ?? "Guest"表达式的结果将为”Guest”,因为左侧的操作数为null,所以使用了默认值”Guest”。

    这些运算符可以一起使用,以处理复杂的空引用场景。

    示例:

    string username = user?.Name ?? "Guest";
    Console.WriteLine(username);
    

    在上面的示例中,如果user为null或者user.Name为null,那么username将被赋值为”Guest”。否则,它将被赋值为user.Name的值。

    这样可以防止使用null值引发异常,并提供一个默认值。

  16. 所谓二进制数据的本质就是字节数组;

    字节数组是存储和传输二进制数据的一种常见方式。

    通过使用字节数组,我们可以有效地处理和管理二进制数据,并在计算机系统中进行传输和存储。

Winform

Winform 注意:

  1. winform中,很多属性和方法的单词首字母都是要大写的,例如:Main()方法。

  2. 如何将一个类在两个文件中定义?

    答: — 利用partial(部分)关键字
    被partial关键字修饰的类可以在不同的文件中被定义,最后每个文件中的定义集合到一起就是这个类的最终定义.

  3. WinForms中,事件函数通常具有两个参数:object senderEventArgs e

    1. object sender:这个参数表示引发事件的对象。当事件被触发时,它指向引发事件的控件或对象。通过sender参数,你可以获得对触发事件的对象的引用,进而进行进一步的操作或判断。
    2. EventArgs e:这个参数包含与事件相关的额外信息。它是一个基类,通常使用派生自它的特定事件参数类,比如MouseEventArgs或KeyPressEventArgs。通过e参数,你可以访问与事件相关的属性、方法和数据。

    假设你有一个按钮的Click事件,当你点击按钮时,事件函数的签名可能如下所示:

    private void button_Click(object sender, EventArgs e)
    {
        // 使用sender参数可以访问到按钮对象
        Button clickedButton = (Button)sender;
    
        // 使用e参数可以访问到与该事件相关的属性或方法
        MessageBox.Show("按钮" + clickedButton.Name + "被点击了!");
    }
    

    通过sender和e参数,你可以在事件函数中获取触发事件的对象和事件相关的信息并进行相应的处理。

  4. Winform中某个组件的事件绑定有两种方式:

    • 法一: 在相应组件处右键属性进入选择某个事件,双击进入进行事件函数的代码编辑。
    • 法二: 在相应组件处右键属性进入选择某个事件,不双击,而是用下拉框复用其它已经写好的事件函数,即代码复用。
  5. winform中某些窗体为什么加上一个Partial关键子,其有什么作用?为什么要加这个关键字?

    • 在C#的WinForms应用程序中,Partial关键字用于实现分部类(Partial Class)。分部类是指将一个类的定义拆分成多个部分,分别定义在不同的源代码文件中,但它们在编译后会被合并成一个完整的类。Partial关键字告诉编译器这个类是分部类的一部分。
    • 使用可视化设计器创建WinForms应用程序时,设计器会自动生成一些代码,如窗体的初始化代码和事件处理程序。这些自动生成的代码通常存储在.designer.cs文件中。手写的代码则通常存储在.cs文件中。通过使用Partial关键字,可以将这两个部分的代码组合成一个类,使其看起来像是一个完整的类。
    • 总之,Partial关键字的作用是将一个类的定义拆分成多个部分,以便更好地管理和组织代码。
  6. 在 WinForms 中,事件绑定处理函数可使用 “+=” 运算符来绑定

    //例如:这表示在 button1 控件上单击时,将调用名为 button1_Click 的方法。
    button1.Click += new EventHandler(button1_Click);
    //总之,WinForms 中事件处理程序的绑定方式有多种,但最常见和最简单的方法是使用 "+=" 运算符。
    

    在WinForms中,当您使用 “+=” 运算符将事件处理程序方法绑定到事件时,不需要在方法名称后面添加小括号。

    //例如,假设您有一个名为 "button1_Click" 的事件处理程序方法,您可以使用以下方式将其绑定到 "button1" 控件的 "Click" 事件:
    button1.Click += button1_Click;
    
    //在这个例子中,我们没有在 "button1_Click" 方法名称后面加上小括号。这是因为我们不想调用这个方法,而是将它作为一个处理程序方法来绑定到 "Click" 事件。
    
    //如果您在方法名称后面添加了小括号,例如:
    button1.Click += button1_Click();
    //这将导致 "button1_Click" 方法被立即调用,并且它的返回值(如果有)将被绑定到 "Click" 事件,而不是 "button1_Click" 方法本身。
    
  7. 在 WinForms 项目中,引用的文件通常存放在项目的输出目录中,而输出目录可以是 bin\Debugbin\Release,具体取决于项目的配置。

    默认情况下,当你构建或运行 WinForms 项目时,生成的可执行文件(例如 .exe 文件)以及项目所依赖的其他文件(例如类库、资源文件等)会被输出到输出目录中。

    输出目录的默认设置可以在 Visual Studio 中的项目属性中进行配置。你可以打开项目属性对话框,然后选择 “生成” 选项卡,在其中你会找到 “输出路径” 选项,用于指定输出目录的路径。

    通常情况下,调试(Debug)配置会将输出目录设置为 bin\Debug,而发布(Release)配置会将输出目录设置为 bin\Release。这是因为在开发过程中,调试配置用于生成带有调试符号和其他调试所需信息的可执行文件,以方便调试和测试。而发布配置则用于生成优化的、不带调试符号的最终发布版本。

    所以,引用文件通常存放在 bin\Debugbin\Release 目录中,取决于当前的项目配置。这样做的好处是,当你构建项目时,引用文件会自动复制到输出目录中,以确保项目能够正确运行所需的依赖项。

    需要注意的是,如果你手动更改了输出目录的设置,引用文件的存放路径也会相应改变。

项目结构

打开winform项目的.sln解决方案文件时的项目结构:

解决方案: 不包含代码,用于组织项目之间关系。

  • properties(文件夹): 定义程序集的属性, 一般只有一个 AssemblyInfo.cs 类文件,用于保存程序集的信息,如名称,版本等,这些信息一般与项目属性面板中的数据对应,不需要手动编写。

  • 引用(文件夹): 引用的目标是文件,而USING的目标是内容,其主要功能是方便引入命名空间,在引入命名空间的当前代码页写程序代码时,可以免除长长的命名空间;

    using的命名空间必须在引用里面存在,即先引用dll文件后才能using.

    若不存在某个命名空间,可以在引用文件夹处右键选择添加引用即可。

  • App.config文件: 项目的配置文件

  • 各种窗体文件【cs类文件】,例子如下:

    • closeState.cs[关闭状态的窗体]
    • deviceStatus.cs[设备状态窗体]
    • Form_Main.cs[主要表单窗体]

    其中对于每个窗体文件:

    第一个文件用来放事件处理,业务以及逻辑相关的代码

    第二个文件用来放窗口的外观等设计相关的代码

    第三个文件则是用来放我们导入的资源,一般不用看。

    form窗体举例:

    form.cs:源代码(处理代码)

    form.designer.cs:窗体布局代码,自动生成对应的控件代码

    form.resx:窗体的资源

  • priogram.cs文件:Main函数即入口文件,程序的启动文件

  • 剩余的其他cs文件(即其他的c#类文件以及辅助的文件)。

创建项目后项目文件夹的项目结构:

  • bin:存放项目的编译结果

    debug:调试版本(慢),release:快,发布版本,

    pdb:位置信息-调试

  • obj:编译过程中生成的中间临时文件,加快编译速度,

    把每个模块生成的文件都保存下来,可以加快编译速度下次编译只会编译修改过的部分

  • .csproj:项目文件,位置引用,双击打开项目

  • .sln:解决方案。

    双击.sln的解决方案文件即可进入vs IDE编辑和运行项目

    sln:在开发环境中使用的解决方案文件。它将一个或多个项目的所有元素组织到单个的解决方案中。此文件存储在父项目目录中.解决方案文件,他是一个或多个.proj(项目)的集合,双击可以打开项目。

    *.sln:(Visual Studio.Solution) 通过为环境提供对项目、项目项和解决方案项在磁盘上位置的引用,可将它们组织到解决方案中。

项目导入本地资源文件

Note: winform项目的导入资源文件不像web项目,其只需要将资源放在项目目录,这里不是这么操作。

  1. 先打开资源面板

    法一: 进入Properties文件夹,选择其中的Resources.resx。

    法二:鼠标移动到winform项目名处,右键选择属性,再选择资源

  2. 进入资源面板后,选择添加资源(选择添加现有文件),再ctrl+s保存一下

  3. 发现winform项目下会生成一个Resources文件夹,其存储了我们刚刚导入的资源文件。

主要用到的命名空间

主要是利用到常用的命名空间中的属性和方法。

System.Windows.Forms

窗体

我们创建的窗体类一般都要继承System.Windows.Forms.Form类,因为其很多功能都帮我们搞定了,比如最大化/最小化,关闭等等

Demo.cs

using System;
using System.Windows.Forms;
using System.Drawing;

//指定程序集的版本号
[assembly:System.Reflection.AssemblyVersion("1.1")]
namespace UKE.Winform.Demo
{	
	//窗体是一个类,其要默认继承自System.Windows.Forms.Form,这样可以直接用到里面的属性和方法
	public class MyForm:System.Windows.Forms.Form{
		//声明控件实例
		private Button btnLoad;
		private PictureBox pbPhoto;
		
		//构造函数(同名)
		public MyForm(){
			//设置当前窗体的窗体标题文字
			this.Text = "Hello C#1.1";
			//初始化控件
			btnLoad = new Button();
			btnLoad.Text = "载入图片";//文字
			btnLoad.Left = 10;//位置
			btnLoad.Top = 10;

			//关联按钮事件
			btnLoad.Click += new EventHandler(this.HandlerLoadClick);//注意形参虽然是方法,但不能加()

			pbPhoto = new PictureBox();
			pbPhoto.Width = this.Width / 2; //宽度和高度
			pbPhoto.Height = this.Height / 2;
			pbPhoto.Left = (this.Width - pbPhoto.Width) / 2; //位置
			pbPhoto.Top = (this.Height - pbPhoto.Height) / 2;
			pbPhoto.BorderStyle = BorderStyle.Fixed3D;//边框样式
			
			
			//在WinForms中,this.Controls是一个属性,它是一个表示窗体中子控件的控件集合。
			//控件集合是一种用于存储和管理窗体中的       控件的容器。通过访问this.Controls属性,可以获取或操作窗体中的子控件。
			//它提供了一些方法和属性,可以用来添加、删除、查找和遍历子控件。
			//使用this.Controls可以对窗体中的子控件进行访问、操作和管理,使得WinForms应用程序中构建用户界面变得灵活和方便。
    			this.Controls.Add(btnLoad);
			this.Controls.Add(pbPhoto);

		}
		
		//事件方法
		private void HandlerLoadClick(object sender,EventArgs e){
			//事件代码(选择图片,打开文件对话框/文件选择框)
			OpenFileDialog dialog = new OpenFileDialog();
			dialog.Title = "请选择图片"; //文件选择框左上角文字
			dialog.Filter = "jpg图片(*.jpg)|*.jpg|所有文档(*.*)|(*.*)";//过滤(前面一个用于前端显示,后面一个才是真正过滤,且后面一个不用加小括号包裹)
			
			if(dialog.ShowDialog() == DialogResult.OK){
				
				//show image
				pbPhoto.Image = new Bitmap(dialog.OpenFile());
			}
			//释放控件、组件或其他可释放资源所占用的资源并进行清理操作。
			//当一个控件或者组件不再需要时,应该显式地调用其Dispose方法,以确保在不需要时及时释放它所占用的资源。=
			dialog.Dispose();
			
		}
		
		//入口方法
		[System.STAThread]
		public static void Main(){
			//启用系统界面风格效果,使窗体的效果与当前操作系统的效果保持一致
			System.Windows.Forms.Application.EnableVisualStyles();
			System.Windows.Forms.Application.Run(new MyForm());//传入窗体实例,作为第一个启动的窗体
		}
	
	}
}

窗体的属性

  • Text: 窗体中的左上角边框的文字显示
  • Icon:窗体的图标
  • WindowState: 窗体刚启动状态,枚举类(Normal, 最大/最小)
  • StartPosition: 窗体启动时的坐标位置
  • Size: 窗体的宽和高
  • ShowInTaskbar: 是否在底部任务栏显示任务标识
  • Visible:是否显示窗体

窗体的方法

  • Hide():窗体的隐藏,
  • Close():窗体的关闭
  • Show():窗体的显示

窗体的事件

  • SizeChanged: 窗体尺寸改变时的事件
  • Load: 窗体载入时的事件。注意窗体类的构造函数执行在前,然后才轮到这个Load事件。

窗体间传递数据

利用控件的Tag属性。

在WinForm中,可以使用Tag属性将数据传递给窗体或控件。

Tag属性是一个对象,它可以存储任何类型的数据。可以在一个窗体或控件上设置Tag属性,然后在另一个窗体或控件中读取它,以实现数据传递。

注意: 在使用Tag属性传递数据时,要确保存储在Tag属性中的数据类型与读取它的控件所期望的类型相匹配,否则可能会导致运行时错误

下面是一个使用Tag属性在两个窗体之间传递数据的示例:

假设有一个主窗体MainForm和一个子窗体ChildForm,并且我们想要在主窗体中设置这个字符串,从而在子窗体中显示一个字符串。

可以按照以下步骤实现:

  1. MainForm中设置一个字符串,并将其存储在Tag属性中:
private void MainForm_Load(object sender, EventArgs e)
{
    string myString = "Hello, world!";
    childForm.Tag = myString;
}
  1. ChildForm中读取Tag属性并显示存储的字符串:
private void ChildForm_Load(object sender, EventArgs e)
{
    string myString = this.Tag as string;
    if (!string.IsNullOrEmpty(myString))
    {
        label1.Text = myString;
    }
}

子窗体调用父窗体的方法

利用Owner属性。

可以通过设定窗体的Owner属性,在打开的窗体中调用其Owner(所有者)窗体的方法。

下面是一个示例。

首先,我们有两个窗体,Form1和Form2。

Form2被Form1打开,所以这里的Form2是子窗体,Form1是父窗体,然后这里有个需求: Form2调用Form1的方法。

我们在Form1中创建一个方法:

public partial class Form1 : Form
{
    //这里是Form1中的一个方法
    public void ShowMessage(string message)
    {
        MessageBox.Show(message, "Message from Form2");
    }
    
    private void Button_Click(object sender, EventArgs e)
    {
        Form2 form2 = new Form2();
        form2.Owner = this;//设置Form2的Owner属性是Form1
        form2.Show();//这里是Form1打开Form2窗口
    }
}

在Form2中,我们使用Owner属性来获取对Form1的引用,并调用Form1的方法:

public partial class Form2 : Form
{

    private void Button_Click(object sender, EventArgs e)
    {
        var form1 = this.Owner as Form1;//得到Form1窗体
        //Form2调用Form1中的方法
        form1?.ShowMessage("Hello, I'm Form2.");
    }
}

以上,在Form1点击Button后打开Form2,然后在Form2点击Button就会调用Form1的ShowMessage方法,显示一个消息框。

控件

控件的通用属性

1. Tag属性

在WinForms中,每个控件都可以使用Tag属性

  • Tag属性是一个通用的对象属性,可以用来存储与控件相关的任意数据。它可以接受任何类型的对象作为值,例如字符串、整数、自定义对象等都可存储在Tag属性中。

  • 通过将数据存储在Tag属性中,开发人员可以在控件上附加自定义信息,以方便应用程序开发和处理。

  • 使用Tag属性时,开发人员可以根据需要将其用作控件的标记或元数据。

  • 通过利用Tag属性,可以方便地将控件与其他相关的实体关联起来,如数据库记录、业务对象等,跨窗体传递数据等。

可以通过以下方式访问和使用Tag属性:

  1. 设置Tag属性值:

    button1.Tag = "按钮1";
    
  2. 获取Tag属性值:

    string tagValue = button1.Tag as string;
    
  3. 使用Tag属性的值进行相关操作:

    if (button1.Tag != null && button1.Tag.ToString() == "按钮1")
    {
        // 执行相关操作
    }
    

需要注意的是,由于Tag属性是一个通用的对象属性,所以在使用它时需要注意类型转换和空值检查,以保证代码的正确性和健壮性。

Tag属性的应用:

  • 有时我们想存一个数据,但又不让它显示,这时就可以利用Tag属性。
  • 窗体间需传递简单数据时,也可利用Tag属性。
  1. TabIndex属性

TabIndex:此属性主要用于确定控件的导航顺序。当您使用Tab键在表单中的不同控件中切换时,TabIndex的值决定了这种切换的顺序。值越小,控件在切换顺序中的位置越靠前。

例如:

Button1.TabIndex = 0;
Button2.TabIndex = 1;

在这个例子中,如果您在此表单上按下Tab键,您将首先导航到Button1,然后导航到Button2。

我们需了解每个控件的:

  • 功能
  • 属性
  • 方法
  • 事件

查看信息:

  • VS中,视图->对象浏览器中可看dll文件中的类属性、方法等
  • MSDN(MicroSoft Developer Network)官网文档的搜索栏可以输入某个类的完全限定名来查询相应的控件信息。

基本控件

  • NotifyIcon:

    功能: 系统栏显示的小图标

    特点: 其不占用窗体的面积,其位于vs界面的左下角。

    属性:

    • Visible: 窗体应用启动时是否显示小图标

    事件:

    • 双击时的事件。

    demo.cs:

    //窗体尺寸改变时添加一个事件
            private void Form1_SizeChanged(object sender, EventArgs e)
            {
                switch (this.WindowState)
                {
                    case FormWindowState.Minimized:
                        this.ShowInTaskbar = false;//窗体缩小时不显示底部任务栏图标
                        this.notifyIcon1.Visible = true;//窗体缩小时显示系统栏小图标
                        break;
                    case FormWindowState.Normal:
                    case FormWindowState.Maximized:
                        this.ShowInTaskbar = true;//窗体最大化显示时显示底部任务栏图标
                        this.notifyIcon1.Visible = false;//窗体最大化显示时不显示底部任务栏图标
                        break;
                    default:
                        break;
                }
            }
    
            private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
            {
                this.WindowState = FormWindowState.Normal;//恢复窗体的正常显示界面
            }
    
  • Label

    功能: 显示文字内容。

    属性:

    • Text: 手动文本编辑,在文本里输入文字以及手动换行等。
  • LinkedLabel

    功能: 表示URL的文字内容显示

    事件:

    • 添加click事件实现链接跳转。
  • TextBox

    功能: 文本框

    属性:

    • Text: 单行文字
    • Multiline:多行文字
    • PasswordChar:手动输入密码时显示的符号
    • UseSystemPasswrodChar: 使用系统输入密码时的显示的符号
  • Button

    功能: 按钮

  • DateTimePicker

    功能: 日期时间选择

  • RadioButton

    功能: 多选一,单选框。RadioButton(单选按钮)是一组互斥的选项,在同一组内只能选择一个。当用户选中一个RadioButton时,其他RadioButton会自动取消选择。

  • CheckBox

    功能: 复选框。CheckBox(复选框)是独立的选项,用户可以同时选择多个CheckBox,它们之间没有互斥的关系。

  • GroupBox

    功能: 用于分组。

列表控件

常用列表控件

ListBox

功能: 用于显示一个可滚动的列表。ListBox可以显示一列项目,用户可以选择其中的一个或多个项目。

属性:

  • Items: ListBox中要显示的项目列表。可以使用Items属性的Add方法添加项目,或者直接在设计器中的items属性窗格中手动输入。
  • SelectionMode:ListBox中项目的选择模式。可以设置为Single(只能选择一个项目)、Multiple(可选择多个项目)或Extended(可选择连续多个项目)。
  • SelectedIndex:当前选定项目的索引。

CompoBox:

功能:C#中的ComboBox是Windows Forms中的一个控件,它提供了一个可编辑的下拉列表,用户可以从中选择一个选项,或者输入自定义的文本.

属性:

  • Name: ComboBox控件的名称,用于在代码中引用它。
  • Items: ComboBox中要显示的选项列表。可以使用Items属性的Add方法添加选项,或者直接在设计器中的属性窗格中输入选项。
  • DropDownStyle:此属性决定了下拉列表的显示方式。DropDownStyle属性有三个选项:
    1. DropDownList:下拉列表框只能选择列表中的项,不能手动输入。用户只能通过点击下拉箭头选择已有的项。
    2. DropDown:下拉列表框可以手动输入,用户可以在文本框中输入自定义的值。用户可以通过点击下拉箭头选择已有的项,或者直接在文本框中输入。
    3. Simple:简单列表框,不包含下拉箭头,直接显示所有的选项。用户可以手动输入,但是无法通过下拉选择。通常用于显示较少的选项。
  • SelectedIndex: 当前选定项的索引。可以通过代码设置它来选择ComboBox中的选项。

事件:

一些常见的事件包括:

  • SelectedIndexChanged: 当选定项发生更改时触发的事件。
  • DropDown: 当下拉列表展开时触发的事件。
  • TextChanged: 当文本框中的文本发生更改时触发的事件。
列表绑定数据:

在WinForms中,ListBox不支持数据源绑定,而ComboBox是支持数据源绑定的。

故ComboBox支持数据源绑定和遍历添加数据。

而ListBox仅仅支持遍历添加数据。

法一:【数据源属性进行绑定】

示例代码(一般利用数据源绑定都要写到下面三行代码):

//下面三行示例代码
comboBox1.DataSource = dataList;//将数据源一次性绑定数据,通过设置DataSource属性,ComboBox会自动从数据源中获取数据,并在下拉列表中显示。
comboBox1.DisplayMember = "Title";//将对象属性作为列表显示的文字,一般是指定sql语句返回的列字段
comboBox1.ValueMember = "ID";//指定对象的值属性作为"值"字段,一般是指定sql语句返回的列字段

法二:【利用遍历循环利用Items属性添加】

ListBox是用来显示列表的控件,它可以通过Items属性手动添加和删除项。

如果要绑定数据源到ListBox,需要手动遍历数据源并将数据添加到ListBoxItems集合中。

示例代码:

List<string> dataList = new List<string> { "Item 1", "Item 2", "Item 3" };

foreach (string item in dataList)
{
    listBox1.Items.Add(item);//遍历添加数据到Items属性
}

PictureBox

PictureBox:

功能:用于显示图像或绘制的图形。PictureBox提供了一种简单的方式来显示图像,并且可以进行基本的缩放、绘制和交互操作。

属性:

  • Name: PictureBox控件的名称,用于在代码中引用它

  • Image: 要显示在PictureBox中的图像。可以使用Image属性来加载图像文件,或者直接在设计器中的属性窗格中选择图像。

  • SizeMode:图像在PictureBox中的显示模式。常见的模式包括AutoSize(自动调整大小以适应图像)、StretchImage(拉伸图像以填充PictureBox)和Zoom(按照图像比例显示,并保持图像完整)等。

事件:

一些常见的事件包括:

  • Click: 当用户单击PictureBox时触发的事件。
  • MouseMove: 当鼠标移动到PictureBox上时触发的事件。
  • Paint: 当PictureBox需要进行绘制时触发的事件。
private void Form_Load(object sender, EventArgs e)
{
    // 加载图像到PictureBox
    pictureBox1.Image = Image.FromFile("image.jpg");
    pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
}

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    // 在标题栏显示鼠标坐标
    this.Text = "鼠标坐标:" + e.X + ", " + e.Y;
}

TreeView

TreeView:

功能:用于显示树形结构的数据。它允许用户展开或折叠节点,以查看或编辑层次数据,通常用于创建树状结构的用户界面。

TreeView 控件可以用来展示分层数据,例如文件系统的目录结构、组织结构图、分类目录等。它以树的形式将数据按照层次结构进行显示,每个节点可以有一个父节点和多个子节点。

属性:

  • Nodes :Nodes属性用来操作树的节点。Nodes 属性是一个 TreeNodeCollection 对象,可用于添加、删除和遍历节点。

demo.cs:

// 创建树节点
TreeNode rootNode = new TreeNode("根节点");
rootNode.Nodes.Add("子节点1");
rootNode.Nodes.Add("子节点2");

// 将根节点添加到 TreeView 控件
treeView1.Nodes.Add(rootNode);

ListView

功能:用于以表格形式显示和操作项的集合。它可以在窗体上展示多列的数据,并提供了选中、排序、编辑等功能。

除了基本的操作外,ListView 还提供了其他功能和事件,如排序、编辑、选择、图标设置、视图切换等,可以根据需求进行进一步的定制和扩展。

ListView控件用于显示列表数据,并且使用ListViewItem类来代表每一行的列表项。

ListViewItem类表示ListView中的一个数据行,它包含了该行的显示文本、图像和其他属性。

要使用ListViewItem,可以按照以下步骤操作:

  1. 创建一个ListViewItem对象,可以通过构造函数传入项的文本或者使用ListView.Items.Add方法添加项,例如:
// 创建一个带有文本的ListViewItem
ListViewItem item1 = new ListViewItem("Item 1");

// 或者使用Add方法添加项
listView1.Items.Add("Item 2");
  1. 设置ListViewItem的图像,可以使用ListViewItem.ImageIndex或者ListViewItem.ImageKey属性,其中:
  • ListViewItem.ImageIndex属性使用图像在设置了ListView的LargeImageList或SmallImageList属性的ImageList中的索引。
  • ListViewItem.ImageKey属性使用图像在设置了ListView的LargeImageList或SmallImageList属性的ImageList中的键值。
// 设置项的图像索引
item1.ImageIndex = 0;

// 设置项的图像键值
item1.ImageKey = "key";
  1. 由于ListViewItem类表示ListView中的一个数据行,其中每行的第一列数据的赋值可以通过其Text属性进行赋值,而其他列的数据赋值只能通过子项SubItems赋值。
//设置Text属性方式给每行的第一列数据赋值
item1.Text = xx;

// 设置子项的方式添加每行的其他列的数据
item1.SubItems.Add("Subitem 1");
  1. 将ListViewItem添加到ListView控件中,可以使用ListView.Items集合的Add方法,例如:
listView1.Items.Add(item1);

这样,ListView控件就会显示相应的ListViewItem项。

例如下面的:Demo.cs

//添加列表项
foreach (Student student in list)
{
    ListViewItem listViewItem = new ListViewItem();
    listViewItem.ImageIndex = 0;
    listViewItem.Text = student.NAME;
    listView1.Items.Add(listViewItem);
}

属性:

  • Columns: 此属性可设置列头信息,指定每列的标题、宽度等。

  • Items: 此属性操作项集合,可以添加、删除和修改项。每个项都可以包含多个子项,使用 SubItems 属性来设置子项数据。

    即每行数据,第一列数据是主项数据,其他各列数据均为子项数据。

  • FullRowSelect: 是否开启整行选中

  • Anchor: 当窗体大小变化时,选择哪个方向的边距始终保持不变。

  • Dock: 边框吸附的位置和方向。

  • View: 此属性用于设置视图模式,决定了如何呈现列表项和子项。

    枚举类型,具有以下几个常用的选项:

    1. LargeIcon:以大图标形式显示列表项。每个列表项可以显示一个大图标和一个文本标题。
    2. SmallIcon:以小图标形式显示列表项。每个列表项仅显示一个小图标和一个文本标题。
    3. List:以列表形式显示列表项。每个列表项显示在单独的行上,并且可以包含多个子项。
    4. Details:以详细信息形式显示列表项。每个列表项以多列的形式显示,并且可以包含多个子项。
    5. Tile:以平铺形式显示列表项。每个列表项显示在单独的区域内,可以包含图标、标题和其他自定义内容。
  • ListView控件提供了两个与图像相关的属性:LargeImageList和SmallImageList属性。

    1. LargeImageList属性:

      • LargeImageList属性是一个ImageList对象,用于设置ListView控件中每个项在大图标视图模式下显示的图像列表。
      • 在大图标视图模式下,ListView的每个项通常会显示一个较大的图标和相关文本信息。
      • 通过将LargeImageList属性设置为一个已经填充了图像的ImageList对象,可以为每个项提供相应的图标。
    2. SmallImageList属性:

      • SmallImageList属性也是一个ImageList对象,用于设置ListView控件中每个项在小图标视图模式下显示的图像列表。
      • 在小图标视图模式下,ListView的每个项通常会显示一个较小的图标和相关文本信息。
      • 通过将SmallImageList属性设置为一个已经填充了图像的ImageList对象,可以为每个项提供相应的图标。

      要使用LargeImageList和SmallImageList属性,可以按照以下步骤操作:

      1. 创建一个ImageList对象,并添加需要显示的图像。
      2. 将LargeImageList属性或SmallImageList属性设置为该ImageList对象。

示例代码如下所示:

using System.Windows.Forms;

namespace ListViewExample
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();

            // 设置 ListView 的 View 属性为 Details,以显示多列数据
            listView1.View = View.Details;

            // 添加列头信息
            listView1.Columns.Add("列1", 100);
            listView1.Columns.Add("列2", 100);

            // 添加项信息
            ListViewItem item1 = new ListViewItem("项1");
            item1.SubItems.Add("子项1.1");

            ListViewItem item2 = new ListViewItem("项2");
            item2.SubItems.Add("子项2.1");

            // 将项添加到 ListView 控件
            listView1.Items.Add(item1);
            listView1.Items.Add(item2);
        }
    }
}

在上面的示例中,我们首先将 ListView 的 View 属性设置为 Details,以显示多列数据。然后,通过 Columns 属性添加了两个列头信息。接下来,创建了两个项(item1 和 item2),并使用 SubItems 属性添加了子项的数据。最后,将项添加到 ListView 控件的 Items 集合中。

通过这样的方式,我们可以构建一个表格样式的控件,显示和操作多列的数据。可以根据需要设置列的显示样式、排序方式,以及利用事件和方法处理用户与 ListView 控件的交互。

DataGridView

What:

DataGridView是Windows Forms中的一个数据网格控件,用于在表格形式中展示和编辑数据。它提供了丰富的功能和灵活的定制选项,可以轻松地与数据源进行绑定并进行常见的数据操作。它允许你以表格的形式展示和编辑数据。它可以帮助你轻松地展示和编辑数据。无论是简单的数据展示,还是复杂的数据操作,DataGridView都能提供很好的支持。

DataGridView控件的主要特点和使用方式:

  1. 数据绑定:
    DataGridView可以通过数据绑定将数据源与控件关联起来。它能够与多种数据源进行绑定,包括DataTable、DataSet、List和自定义的数据集合等。通过数据绑定,DataGridView可以自动显示和更新数据。

    //利用DataSource属性即可,下面代码是绑定到一个DataTable对象
    dataGridView1.DataSource = dataTable1;
    
  2. 列定义:
    DataGridView允许你自定义列的类型和外观。它提供了多种列类型,包括文本列、按钮列、复选框列、图片列等。你可以通过代码或设计器来定义列,并设置各种属性,如标题、宽度、格式、排序等。

  3. 数据编辑:
    用户可以直接在DataGridView中编辑单元格的值。可以通过设置ReadOnly属性来控制指定单元格、整行或整个控件是否可编辑。你还可以自定义编辑器来实现更复杂的编辑操作,例如日期选择器或下拉列表框。

  4. 排序和筛选:
    DataGridView支持对数据进行排序和筛选。你可以通过设置列的排序模式和使用内置的排序和筛选功能来实现这些操作。用户可以点击列标题来进行排序,并可以使用自定义的筛选器来筛选数据。

  5. 样式和外观定制:
    你可以通过设置DataGridView和其元素(如单元格、行、列、网格线等)的样式和外观属性来实现定制化。你可以调整颜色、字体、背景、边框等,以满足你的设计需求。

  6. 事件处理:
    DataGridView提供了多个事件来处理用户交互和数据操作。你可以订阅这些事件,例如CellClickCellValueChangedRowValidating等,以响应特定的用户操作和数据变化。

  7. 导出和打印:
    DataGridView支持将数据导出为其他文件格式,例如Excel、CSV等。你可以使用内置的ExportToExcel方法或第三方库来实现导出。此外,DataGridView还提供了打印功能,用于将数据打印为纸质文档。

布局容器

Panel(面板)

  • 其可以类比为做网页时的DIV标签一样,容器的作用。

  • 程序运行时面板的边界是看不到的。

在WinForms中,Panel是一个容器控件,用于容纳其他控件。它是一个可见的矩形区域,可以用来组织、布局和管理其他控件。

Panel通常用于创建自定义的用户界面或划分页面的区域。

以下是Panel控件的一些特点和用法

  1. 容器控件:Panel本身并没有显示任何可见内容,而是用于容纳其他控件。可以将其他控件(如按钮、文本框等)添加到Panel中,从而形成一组有序的控件组合。
  2. 布局和组织:Panel可以帮助将控件按照需要的布局进行组织。通过设置Panel的大小、位置和边距等属性,可以确定Panel内部控件的位置和相对关系。这样可以更灵活地管理界面元素的布局。
  3. 可滚动区域:Panel可以作为一个可滚动的区域,当Panel内的控件超过Panel的显示区域时,可以启用自动滚动功能,以便用户可以通过垂直或水平滚动条查看全部内容。
  4. 视觉效果:Panel可以设置背景颜色、边框样式以及背景图片等属性,以实现一些视觉效果。可以使用独立的图像或者渐变的颜色作为Panel的背景。
  5. 事件处理:Panel控件可以对鼠标和键盘事件进行响应,并且可以通过事件处理器进行自定义操作。例如,可以通过点击Panel上的按钮来触发事件,并执行相应的操作。

以下是Panel一些常用的属性:

  • Size:设置Panel的大小。
  • Location:设置Panel在容器中的位置。
  • BackColor:设置Panel的背景颜色。
  • BorderStyle:设置Panel的边框样式。
  • AutoScroll:设置是否启用Panel的自动滚动功能。
  • Controls:Panel的子控件集合,通过该集合添加或移除其他控件。

总结:Panel控件是WinForms中常用的容器控件,用于组织和管理其他控件。它提供了布局、滚动、背景设置等功能,使得界面的设计和布局更加灵活和易于管理。

GroupBox

GroupBox是一个容器控件,用于将其他相关的控件组合在一起,并提供一个可选的标题。

GroupBox常用于对相关的选项或功能进行分组,一起显示在界面上。

以下是GroupBox控件的一些特点和用法:

  1. 容器控件:GroupBox本身不会显示任何内容,而是用于容纳其他控件。可以将其他控件(如按钮、文本框等)添加到GroupBox中,将它们作为一组显示。

  2. 框架和标题:GroupBox提供一个可选的框架,以突出显示组合控件的区域,并使用标题标识该区域的目的或内容。

    可以通过设置GroupBox的Text属性来设置标题文本。

  3. 布局和组织:GroupBox可以帮助将相关的控件按照需要的布局进行组织。

    通过设置GroupBox的大小、位置和边距等属性,可以确定GroupBox内部控件的位置和相对关系。

  4. 可视化风格:GroupBox可以设置边框样式、边框颜色以及背景颜色等属性,以实现对GroupBox控件的视觉风格定制。可以根据需要选择不同的外观风格。

  5. 事件处理:GroupBox控件可以对鼠标和键盘事件进行响应,并且可以通过事件处理器进行自定义操作。例如,可以使用Click事件来处理GroupBox被点击时所触发的操作。

以下是一些常用的属性:

  • Size:设置GroupBox的大小。
  • Location:设置GroupBox在容器中的位置。
  • Text:设置GroupBox的标题文本。
  • BackColor:设置GroupBox的背景颜色。
  • BorderStyle:设置GroupBox的边框样式。
  • Controls:GroupBox的子控件集合,可以通过该集合添加或移除其他控件。

总结:GroupBox控件是C#中常用的容器控件,用于将相关的控件组合在一起,并提供一个标题文本。

它可以帮助组织界面上的控件,并提供一定的视觉风格和布局控制。

FlowLayoutPanel

FlowLayoutPanel是一个容器控件,用于在一个自动布局的面板中按照流动的方式排列其内部的子控件。

FlowLayoutPanel的主要特点和用法如下:

  1. 自动布局:FlowLayoutPanel会根据容纳的子控件的大小和内容进行自动布局,按照流动的方式从左到右、从上到下排列子控件。它会自动调整子控件的位置和大小,以适应面板的大小变化。
  2. 易于响应窗口调整:当FlowLayoutPanel的大小发生变化时,它会自动重新排列子控件,以适应新的布局。这使得用FlowLayoutPanel创建自适应布局的界面变得更加简单。
  3. 自定义布局:可通过设置FlowLayoutPanel的FlowDirection属性来指定子控件的排列方向,可选择水平(从左到右)或垂直(从上到下)排列。还可以通过设置Padding和Margins属性来指定子控件之间的间隔和边距。
  4. 容纳各种控件:FlowLayoutPanel可以容纳各种类型的控件,并可与其他布局控件(如按钮、文本框、标签等)组合使用。
  5. 动态控件添加和移除:可以通过FlowLayoutPanel的Controls集合添加或移除子控件。添加控件时,FlowLayoutPanel会自动调整布局,确保新控件正确的排列。

以下是一些常用的属性:

  • FlowDirection:获取或设置子控件的排列方向(水平或垂直)。
  • AutoScroll:获取或设置是否启用自动滚动功能。
  • WrapContents:获取或设置是否允许子控件在容器边界处换行。
  • Padding:获取或设置子控件之间的内边距。
  • Controls:FlowLayoutPanel控件的子控件集合,可以通过该集合添加或移除其他控件。

通过上述属性,可以根据需要创建和自定义FlowLayoutPanel控件,实现自动排列子控件的自适应布局。

总结:FlowLayoutPanel是C#中常用的容器控件,用于按照流动的方式自动布局其内部的子控件。它简化了界面布局的过程,使得创建自适应布局的界面变得更加容易和灵活。

TableLayoutPanel

用于以表格的形式排列和组织其内部的子控件。

TableLayoutPanel的主要特点和用法如下:

  1. 表格布局:TableLayoutPanel使用表格的形式来布局和组织子控件。可以指定行数和列数,并在特定的行和列位置放置子控件。
  2. 灵活的单元格布局:TableLayoutPanel的每个单元格可以根据需要调整大小,以适应其内部控件的大小。每个单元格可以独立设置列宽和行高,也可以使用百分比来调整单元格的大小。
  3. 控件位置和对齐:可以通过设置子控件的Row和Column属性,将其放置在TableLayoutPanel的指定单元格中。还可以使用Anchor和Dock属性来对齐子控件在单元格中的位置。
  4. 自动调整布局:TableLayoutPanel可以在窗口大小发生变化时自动调整子控件的布局。这使得TableLayoutPanel适用于创建自适应性强的窗体界面。
  5. 支持嵌套:TableLayoutPanel可以嵌套使用,即在一个单元格中放置另一个TableLayoutPanel。这样可以创建更复杂的布局结构。

使用TableLayoutPanel控件需要注意以下几点:

  • 配置行和列:通过TableLayoutPanel的RowCount和ColumnCount属性,可以设置表格的行数和列数。
  • 设置行高和列宽:通过调整TableLayoutPanel的RowStyles和ColumnStyles属性,可以设置每行和每列的高度和宽度。可以使用固定像素值、百分比或自动大小来设置。

实例化和使用TableLayoutPanel可以通过设计器进行配置,也可以通过编程方式进行设置。以下是一些常用的属性和方法:

  • RowCount和ColumnCount:获取或设置行数和列数。
  • RowStyles和ColumnStyles:获取或设置每行和每列的样式(高度和宽度)。
  • Controls:TableLayoutPanel的子控件集合,可以通过该集合添加或移除其他控件。

总结:TableLayoutPanel是WinForms中常用的容器控件,用于以表格的形式布局和组织子控件。它提供了灵活的表格布局功能,可以根据需要自定义行和列,并自动调整子控件的布局。使用TableLayoutPanel可以创建出结构清晰、自适应性强的窗体界面。

TabControl

TabControl是一个容器控件,用于在一个选项卡式的界面中切换和显示不同的子控件。

TabControl的主要特点和用法如下:

  1. 选项卡式界面:TabControl提供了一个选项卡控件,用于显示多个标签页。每个标签页可以关联一个子控件,并在用户选择相应的标签页时显示该子控件。
  2. 多标签页管理:TabControl允许在运行时添加和删除标签页。可以通过代码动态地添加和移除标签页,以便根据需要调整界面结构。
  3. 导航和切换:用户可以通过点击选项卡来切换显示不同的子控件。TabControl会自动切换当前显示的子控件,并触发一些相关事件,以便应用程序可以响应用户的操作。
  4. 定制样式:可以通过设置TabControl的外观相关属性,如背景颜色、字体、边框样式等,来定制TabControl的外观和风格。
  5. 事件处理:TabControl可以触发一些事件,如SelectedIndexChanged事件,可在切换标签页时执行自定义操作。

以下是一些常用的属性:

  • TabPages:TabControl的标签页集合,可以通过该集合添加、移除和操作标签页。
  • SelectedIndex:获取或设置当前选中的标签页的索引。
  • SelectedTab:获取或设置当前选中的标签页。
  • Alignment:获取或设置标签的对齐方式(顶部、底部、左侧或右侧)。
  • Multiline:获取或设置是否支持多行标签。

总结:TabControl是WinForms中常用的容器控件,用于实现一个选项卡式的界面,通过切换选项卡显示不同的子控件。它提供了多标签页管理、定制样式、导航和切换等功能,使界面切换和导航变得简单直观。

SplitContainer

在WinForms中,SplitContainer是一个容器控件,用于将窗体分割成两个可调整大小的面板。

SplitContainer的主要特点和用法如下:

  1. 分割面板:SplitContainer将窗体分割为两个面板,一个水平的或垂直的分割面板。可以通过拖动分隔条来调整面板的大小,从而控制它们的相对大小。
  2. 可调整大小:SplitContainer允许用户根据需要调整面板的大小。可以通过拖动分隔条来改变面板大小,以适应用户对界面布局的需求。
  3. 嵌套面板:SplitContainer可以嵌套使用,即在一个面板中放置另一个SplitContainer。这样可以创建更复杂的层级结构,实现更灵活的布局。
  4. 自动大小调整:SplitContainer可以根据窗体的大小调整其内部面板的大小。这使得创建自适应布局的界面变得更加简单。
  5. 事件处理:SplitContainer提供了一系列事件,如SplitterMoving、SplitterMoved等,用于处理分隔条的移动和调整大小的操作。可以通过事件处理器来自定义操作。

以下是一些常用的属性:

  • Orientation:获取或设置分割面板的方向(水平或垂直)。
  • FixedPanel:获取或设置一个固定大小的面板,不会随分隔条的移动而改变大小。
  • Panel1和Panel2:分割面板中的两个面板,可以通过这两个属性来访问和操作。
  • SplitterDistance:获取或设置分隔条离SplitContainer容器的边的距离。

总结:SplitContainer是WinForms中常用的容器控件,用于将窗体分割为两个可调整大小的面板。它提供了面板的分割和调整大小的功能,并支持嵌套使用。使用SplitContainer可以实现界面的分割布局和面板的自适应调整,使得界面布局更加灵活和可调整。

菜单和工具栏

  • MenuStrip: 菜单栏

  • StatusStrip:状态栏

  • ToolStrip:工具栏

  • ContextMenuStrip:上下文菜单栏(右键菜单)

    (不同位置右键出来的菜单不一样)

    注意:

    要让组件的ContextMenuStrip属性与此右键菜单进行绑定。

注意Note:

  1. 输入英文输入下的-符号可以插入一个分割横线。

  2. 菜单栏上小括号里的大写英文字母是助记符,利用Alt+英文字母的快捷键可以打开相应的菜单栏。

    在利用UI进行助记符的文本编辑时,除了直接输入(F),还需要在前面加上一个&符号,即: &(F)才可以。

  3. 如果要在子菜单中增加快捷键的设置,可以在相应的菜单中右键选择属性,进入ShortcutKeys属性进行设置,如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4i7NcULg-1691668028059)(winform.assets/image-20230727134640158.png)]

  4. 如果要在子菜单中增加一个图标,可以在相应的菜单中右键选择属性,进入Image属性进行设置,如下:

  5. 添加一个关于菜单的窗体,可以不用直接新建窗体,而可以通过项目右键选择添加,再选择新建项,选择Windows Forms,选择关于Box即可新建,如下:


对于关于框中相关信息的修改,可以通过项目右键选择属性,再选择程序集信息,进入编辑即可。

ADO.NET

.Net(framework)和ADO.Net

  1. 什么是ADO.Net?

    1. ADO.NET(ActiveX Data Objects .NET)是为Microsoft .NET平台提供的一组数据访问技术,用于在.NET应用程序中与各种数据源进行交互。ADO.NET是.NET Framework中的核心组件之一,用于连接和操作数据库。
    2. 它通过提供一组类和接口,允许开发人员使用统一的编程模型来访问和操作这些数据源。
    3. ADO.NET是.NET Framework中的数据访问技术,提供了用于连接、查询、更新和操作各种数据源的一组类和接口。它通过连接对象、命令对象、数据阅读器、数据适配器等组件,提供了灵活、高性能和可扩展的方式来处理数据库交互。ADO.NET的特点包括对多种数据源的支持、数据缓存、事务处理和参数化查询等功能,大大简化了.NET应用程序与数据库的交互过程。
  2. ADO.NET的主要组件和特点?

    1. 连接(Connection):ADO.NET提供了连接对象,用于与数据源建立连接。开发人员可以使用提供程序特定的连接对象(如SqlConnection、OracleConnection等)来创建和管理与数据库的连接。

    2. 命令(Command):ADO.NET的命令对象用于执行SQL语句或存储过程。通过命令对象,开发人员可以向数据源发送SQL查询或更新语句,并接收返回的结果。

    3. 数据阅读器(DataReader):DataReader是只进只读的结果集,用于高效地读取和处理大型数据集。当执行查询操作时,开发人员可以通过DataReader逐行读取结果,而无需将全部数据加载到内存中,这个对象不是new出来的,一般作为结果返回而间接创建的。

      如下:

      每次指向某一行数据,再根据索引取出其中某列数据。

    4. 数据适配器(DataAdapter):数据适配器负责将数据库中的数据以DataSet(数据集)的形式缓存在客户端内存中。它通过填充DataSet中的DataTable对象,将数据库中的数据转换为应用程序可以操作的格式。

    5. 数据集(DataSet):DataSet是ADO.NET中的核心数据容器。它是一个内存中的数据集合,可以包含多个DataTable对象,并且支持数据之间的关系和约束。

      即: 不管数据源是什么,DataSet的作用是将其数据复制一份到内存中,其存储了多张数据表(DataTable),里面的行对象称为DataRow对象,里面的列对象称为DataColumn对象。

    6. 事务(Transaction):ADO.NET支持事务处理,用于执行一系列数据库操作的原子性提交或回滚。通过Transaction对象,可以管理多个命令或数据库操作的事务性。

    7. 参数化查询:ADO.NET提供了参数化查询的功能,可以安全地构建并执行包含参数的SQL语句。参数化查询可以防止SQL注入攻击,并提高性能和可维护性。

直连式

  1. 什么是直连式?

    即读取数据的过程中数据库的连接是不能关闭的,说明数据并不直接存在于内存,而是数据库。

  2. 有什么优劣?

    优点:

    • 速度快

    缺点:

    • 需要持续保持数据库的连接,如果并发高的情况下,则会保持很多的数据库连接,影响并发效果。
    • 读取数据的指针只能一个一个即一行一行的往下移。
步骤

一: 添加动态链接:

Visual Studio 中选择项目-> 管理NuGet程序包(N) -> 然后在浏览里面搜索 MySql.Data 并进行安装:

注意

因为 .Net 框架 和 MySQL 版本问题安装时可能要选用不同的版本


二: 测试连接

  1. 创建一个类来放数据库的连接和各种操作,我这里创建的是 Dao.cs
  2. 加入需要的头文件:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MySql.Data.MySqlClient; // 这个是必须的
  1. 连接数据库,并返回一个 MySqlConnection 对象;并实现基本操作;
namespace BookMS
{
    class Dao
    {
        MySqlConnection conn;
        // String sql = $"SELECT * FROM t_admin WHERE id = '{textBox1.Text}' and pwd = '{textBox2.Text}'";
      public MySqlConnection getConn()
        {
            string connstr = "server=127.0.0.1;port=3306;user=root;password=****;database=bookdb;Charset=utf8;";
          	// 其中server是数据库所在网络的IP地址,127.0.0.1代表本地,port是数据库设置的端口号,user用户名,password密码,database是要连接的数据库名,Charset=utf8主要是处理中文编码问题
            conn = new MySqlConnection(connstr);
            conn.Open(); // 打开数据库
            return conn; // 返回数据库对象
        }
        public MySqlCommand command(string sql)
        {
            MySqlCommand cmd = new MySqlCommand(sql, getConn());
            return cmd;
        }
        public int Execute(string sql) // 更新操作
        {
            return command(sql).ExecuteNonQuery();
        }
        public MySqlDataReader read(string sql) // 读取操作
        {
            return command(sql).ExecuteReader();
        }

        public void DaoClose() // 关闭数据库连接
        {
            conn.Close(); 
        }
    }
}
  1. 查询操作
MySqlConnection conn = getConn(); //连接数据库
conn.Open(); //打开数据库连接
string sql = "select * from user'" ;
MySqlCommand cmd = new MySqlCommand(sql, conn);
MySqlDataReader mdr = cmd.ExecuteReader();
if (mdr.Read()) {
    string str = mdr["authority"].ToString();//接收一个返回值
    //对查询到数据进行相应的操作
}
conn.Close();
  1. 增删改操作类似,只是 sql 语句不相同,这里以插入操作为例
MySqlConnection conn = getConn(); //连接数据库
conn.Open(); //打开数据库连接
string sql = "insert into user values('123','123')";
MySqlCommand cmd = new MySqlCommand(sql, conn);
cmd.ExecuteNonQuery();
conn.Close();

说明:在进行对数据库具体操作时建议在 Dao 类中直接写不同的函数来实现,在Form代码处创建 Dao 对象来引用对应的方法,这样可以使代码更加简洁,同时也方便维护

Demo

demo.cs

using MySql.Data.MySqlClient;//引入mysql连接客户端

private void Form1_Load(object sender, EventArgs e)
{
    //使用数据库中的数据填充listBox
    DataBindings();
}

/// <summary>
/// 连接数据库获取数据到ListBox中
/// </summary>
private void DataBindings()
{
    //1.创建数据库连接
    string connstr = "server=127.0.0.1;port=3306;user=root;password=root;database=dwiot_cloud;Charset=utf8;";
    // 其中server是数据库所在网络的IP地址,127.0.0.1代表本地,port是数据库设置的端口号,user用户名,password密码,database是要连接的数据库名,Charset=utf8主要是处理中文编码问题
    MySqlConnection conn = new MySqlConnection(connstr);
    try
    {
        //2. 打开数据库
        conn.Open();

        //3. 创建sql语句和执行命令的对象
        string sql = "select id,username from sys_user";
        MySqlCommand cmd = new MySqlCommand(sql, conn);

        //4. 执行查询语句,声明结果集对象,用于检索数据
        MySqlDataReader mdr = cmd.ExecuteReader();

        //遍历mdr数据,判断有无数据可读
        if (mdr.HasRows)
        {
            while (mdr.Read())
            {
                listBox1.Items.Add(mdr[1]);
            }
        }
        else
        {
            MessageBox.Show("根本没数据");
        }
    }catch(Exception e){
        MessageBox.Show("数据库操作遇到异常:{0}", e.Message);
    }
    finally
    {
        if(conn.State != ConnectionState.Closed)
        {
            //关闭连接
            conn.Close();
        }

    }

注意:

  • 利用直连式连接数据库,需要注意写:

    数据库连接对象的Open()方法

    数据库连接对象的Close()方法

  • 若直接式连接数据库,且使用了using语句,则可以不用手动写数据库连接的Close()方法。

断开式

即主要通过DataAdapter对象将数据copy到内存中DataSetDataTable中,数据就从DataTable中获取,而不是一直从数据库中获取。

注意:

  • 使用了DataAdapter对象,其会自动帮我们处理数据库连接对象的打开和关闭,所以我们在代码中不需要写关于连接对象的打开和连接对象关闭等的代码。
Demo

demo.cs:

using MySql.Data.MySqlClient;
using System.Configuration;

//读取配置文件连接字符串字段
private string strConn = ConfigurationManager.ConnectionStrings["DB"].ConnectionString;

//数据库数据放到DataTable,再将DataGridView的数据源绑定到DataTable
private void BindData()
{
    try
    {
        //创建数据库连接对象
        MySqlConnection conn = new MySqlConnection(strConn);

        //书写sql,创建命令对象并赋值
        string sql = "select id,username,realname,password,salt from sys_user";
        MySqlCommand cmd = new MySqlCommand(sql, conn);

        //创建DataAdapter对象,传递Command对象
        //DataAdapter对象可以自动控制数据库连接对象的开和关
        MySqlDataAdapter mda = new MySqlDataAdapter();
        mda.SelectCommand = cmd;

        //创建空白的DataTable对象,用于存储和copy数据库的数据
        DataTable db = new DataTable();


        //填充数据库数据到DataTable中

        mda.Fill(db);

        //将DataTable数据作为DataGridView的数据
        dataGridView1.DataSource = db;
    }catch(MySqlException e)
    {
        MessageBox.Show("error:" + e.Message);
    }

}

配置连接字符串到配置文件

  1. App.config配置文件中配置与数据库连接相关的连接字符串信息:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<!--数据库连接字符串,名字叫做DB-->
	<connectionStrings>
		<add name="DB" connectionString="server=127.0.0.1;port=3306;user=root;password=root;database=dwiot_cloud;Charset=utf8;" providerName="MySql.Data.MySqlClient" />
	</connectionStrings>
</configuration>
  1. cs文件中读取字符串信息:
//先确保项目目录下的引用文件夹下有这个类,否则这里using也白搭
using System.Configuration;//读取配置文件引用文件

//读取配置文件中名字叫做DB的字符串数据内容的ConnectionString字段的数据
private string strConn = ConfigurationManager.ConnectionStrings["DB"].ConnectionString;

参数化查询(防sql注入)

参数化查询是一种防止 SQL 注入攻击的重要技术,也可以提高查询性能。通过使用参数化查询,可以将输入的用户数据安全地传递给 SQL 查询语句,而不是直接将用户数据嵌入到 SQL 语句中。这样可以防止恶意用户输入恶意的 SQL 代码。

在 WinForms 中使用参数化查询,需要使用 ADO.NET 提供的 MySQL 数据库连接对象(MySqlConnection)、命令对象(MySqlCommand)以及参数对象(MySqlParameter)。

使用步骤:

步骤 1:添加引用和命名空间
在项目中的引用文件夹中添加引用,并在代码文件的顶部添加以下命名空间引用:

using MySql.Data.MySqlClient;

步骤 2:创建数据库连接
在代码中创建 MySqlConnection 对象来建立到 MySQL 数据库的连接。需要提供 MySQL 数据库的连接字符串,该字符串包含了数据库服务器的地址、用户名、密码和数据库名称等信息。

string connectionString = "server=127.0.0.1;port=3306;user=root;password=root;database=dwiot_cloud;Charset=utf8;";
MySqlConnection connection = new MySqlConnection(connectionString);

步骤 3:创建参数化查询
创建 MySqlCommand 对象,并设置 SQL 查询语句,其中使用 @parameter 形式的参数占位符来代替实际的参数值

//1.使用 `@parameter` 形式的参数占位符来代替实际的参数值
string query = "SELECT * FROM products WHERE name LIKE @searchTerm";
//2.创建MysqlCommand实例对象
MySqlCommand command = new MySqlCommand(query, connection);
//3.利用MysqlCommand对象.Parameters.AddWithValue方法
//   第一个参数是: 对应的@parameter占位符
//   第二个参数是: 这个占位符实际的取值
command.Parameters.AddWithValue("@searchTerm", "%" + searchTerm + "%");

步骤 4:查询数据库
执行查询语句并获取查询结果。

DataTable table = new DataTable();
MySqlDataAdapter adapter = new MySqlDataAdapter(command);
adapter.Fill(table);

步骤 5:处理查询结果
将查询结果作为数据源绑定到 WinForms 控件上,例如 DataGridView 控件,以显示查询结果。

//绑定数据
dataGridView1.DataSource = table;

完整示例代码如下:

using System;
using System.Data;
using System.Windows.Forms;
using MySql.Data.MySqlClient;



private void MainForm_Load(object sender, EventArgs e)
{
    string connectionString = "server=127.0.0.1;port=3306;user=root;password=root;database=dwiot_cloud;Charset=utf8;";
    connection = new MySqlConnection(connectionString);

    try
    {
        connection.Open(); // 打开数据库连接
    }
    catch (Exception ex)
    {
        MessageBox.Show("无法连接到数据库:" + ex.Message);
        return;
    }
}

private void btnSearch_Click(object sender, EventArgs e)
{
    string searchTerm = txtSearchTerm.Text;
	//参数化sql语句(格式:@+变量名)
    string query = "SELECT * FROM products WHERE name LIKE @searchTerm";

    MySqlCommand command = new MySqlCommand(query, connection);
    //参数化sql语句(格式:@+变量名)
    command.Parameters.AddWithValue("@searchTerm", "%" + searchTerm + "%");

    try
    {
        DataTable table = new DataTable();
        MySqlDataAdapter adapter = new MySqlDataAdapter(command);
        adapter.Fill(table);

        dataGridView1.DataSource = table; // 将查询结果显示在 DataGridView 控件中
    }
    catch (Exception ex)
    {
        MessageBox.Show("查询出错:" + ex.Message);
    }finally{
        connection.Close(); // 关闭数据库连接
    }
}

//在上述示例中,首先创建了一个 WinForms 应用程序,并在窗体加载时建立与 MySQL 数据库的连接。然后,在按钮点击事件中执行参数化查询,将查询结果绑定到 DataGridView 控件上。最后,在窗体关闭时关闭数据库连接。用户可以在文本框中输入查询条件,并点击按钮来进行查询操作。

//通过使用参数化查询,我们将用户输入的 searchTerm 值以参数的形式传递给查询语句,避免了 SQL 注入攻击的风险,并且保证了查询的安全性和性能。

LINQ

  1. 什么是LINQ?

答:LINQ(Language-Integrated Query)(集成语言查询)是一种用于.NET平台的查询和数据操作技术。

它是由微软开发的一组语言扩展和API,用于在编程语言中执行数据查询、转换和操作。LINQ 提供了一种统一的查询语法,称为查询表达式,让开发人员可以使用简洁的语法来编写查询表达式,而无需将查询逻辑嵌入到编程语言的代码中。这种语法类似于SQL语句,但在编译时进行类型检查,并集成到编程语言中。使用查询表达式,开发人员可以以声明式的方式指定查询逻辑,而不必显式编写循环和条件语句。

LINQ 的概念基于一组通用的查询操作,如过滤、排序、分组、投影和聚合等。

它可以用于访问各种数据源,包括对象集合(例如数组和列表)、数据集(例如数据库表)和XML文档等。

LINQ 还提供了一组强大的标准查询操作符,如Where、OrderBy、GroupBy、Select、Join、Aggregate等,开发人员可以使用这些操作符来构建复杂的查询。此外,LINQ 还支持扩展方法语法,允许开发人员通过链式调用方法来编写查询。

总之,LINQ 是一种强大的查询和数据操作技术,简化了开发人员对各种数据源进行查询和操作的过程,提高代码的可读性可维护性。

  1. 关系图:


3. 简单Demo.cs:

LINQ查询表达式

LINQ查询表达式是一种使用类似SQL的语法结构进行数据查询和转换的工具,它是.NET框架中的一个重要组成部分。

使用LINQ查询表达式可以方便地对各种数据源(如数组、集合、数据库表等)进行查询、筛选、排序、分组等操作,同时还能够进行数据转换和投影。

demo1.cs:

//下面是一个简单的LINQ查询表达式的示例:
//假设有一个包含若干个整数的数组,我们想要筛选出其中所有的偶数:

int[] numbers = { 1, 2, 3, 4, 5, 6 };
IEnumerable<int> evenNumbers = from num in numbers
                               where num % 2 == 0
                               select num;
//在这个查询表达式中,我们使用了关键字“from”、“where”和“select”来描述查询的过程。
//其中,“from”关键字用于指定数据源,这里是整数数组“numbers”;
//“where”关键字用于指定筛选条件,这里是“num % 2 == 0”,即判断数字是否为偶数;
//“select”关键字用于指定查询结果的输出形式,这里是直接输出筛选出来的数字本身。

demo2.cs

//另外,LINQ查询表达式还可以进行更加复杂的数据处理操作,例如:
var students = new List<Student>
{
    new Student { Name = "Alice", Age = 20, Gender = Gender.Female },
    new Student { Name = "Bob", Age = 22, Gender = Gender.Male },
    new Student { Name = "Charlie", Age = 18, Gender = Gender.Male },
    new Student { Name = "David", Age = 19, Gender = Gender.Male },
    new Student { Name = "Eve", Age = 21, Gender = Gender.Female },
};

var query = from student in students
            where student.Age >= 20
            group student by student.Gender into genderGroups
            select new
            {
                Gender = genderGroups.Key,
                AverageAge = genderGroups.Average(s => s.Age),
                Names = string.Join(", ", genderGroups.Select(s => s.Name)),
            };

foreach (var result in query)
{
    Console.WriteLine($"{result.Gender}: {result.Names} (avg. age = {result.AverageAge})");
}

//在这个示例中,我们使用了LINQ查询表达式对一个名为“students”的学生列表进行了处理,首先筛选出了年龄大于等于20岁的学生,然后按照性别进行了分组,最终输出了每个性别的学生名单和平均年龄。
//这里使用了“group by”关键字来进行分组操作,并对每个分组进行了聚合计算和投影输出。

LINQ查询表达式是.NET框架中非常强大和灵活的数据处理工具,能够方便地进行各种数据查询和转换操作,提高开发效率和代码质量。

LINQ to Object步骤

  1. 引用 LINQ 命名空间:在代码文件的顶部,使用 using 关键字引用 System.Linq 命名空间,以便使用 LINQ 相关的类型和扩展方法。

  2. 创建数据源:定义一个数据源,可以是对象集合、数组、数据集或其他实现了相应接口的数据结构。

  3. 构建 LINQ 查询表达式:使用 LINQ 的查询语法或扩展方法语法来构建查询表达式,指定要过滤、排序、投影等的操作。

  4. 执行查询:通过调用适当的查询操作符来执行查询。

    查询操作可以是即时执行的(立即返回结果),也可以是延迟执行的(在需要时才计算结果)。

  5. 处理查询结果:使用循环、条件语句或其他操作来处理查询结果,并获取所需的数据。

下面是简单的 LINQ To Object示例,演示如何对一个整数列表进行过滤和排序:

using System;
using System.Linq;//引入LINQ

public class Program
{
    public static void Main()
    {
        // 创建数据源
        int[] numbers = { 5, 1, 4, 2, 3 };

        // 构建 LINQ 查询表达式
        var query = from num in numbers
                    where num % 2 == 0 // 过滤偶数
                    orderby num descending // 按降序排序
                    select num; // 投影选择的数字

        // 执行查询并处理结果
        foreach (var num in query)
        {
            Console.WriteLine(num);
        }
    }
}

//以上示例代码使用 LINQ 查询语法,从一个整数数组中过滤出偶数,并按降序排序。然后通过循环打印出筛选后的结果。
//通过以上步骤,你可以开始使用 LINQ 来进行各种查询和数据操作。

Visual Studio 2019连接MySQL数据库

  1. 要想在 Visual Studio 2019中使用MySQL数据库,首先需要下载MySQL的驱动:

    • mysql-connector-odbc-8.0.20-winx64.msi
      链接: https://dev.mysql.com/downloads/connector/odbc/.

    • mysql-for-visualstudio-1.2.9.msi
      链接:https://dev.mysql.com/downloads/windows/visualstudio/

    • mysql-connector-net-8.0.20.msi
      链接:https://dev.mysql.com/downloads/connector/net/8.0.html
      自行下载即可
      下载完后按照以上顺序安装

  2. 安装完后重启visual studio

    然后点击菜单栏的视图->服务器资源管理器

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WY5Lt5fV-1691668028062)(winform.assets/image-20230731105632781.png)]

    然后数据连接->添加连接

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0fYlCRmD-1691668028062)(winform.assets/image-20230731105710125.png)]

    你就会发现有MySQL 的选项了,进入里面配置数据库相关信息即可。若还是没有MySQL选项,尝试选择更高版本的framework

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xumapLLB-1691668028063)(winform.assets/image-20230731105757303.png)]

Visual Studio 2019 中找不到Linq to SQL 类

在 Visual Studio 2019 中,Linq to SQL 已被标记为“过时”的技术,但仍然可以使用。如果您在 Visual Studio 2019 中找不到 Linq to SQL 类,请按照以下步骤操作:

  1. 确保已安装 .NET 框架版本 3.5 或更高版本。
  2. 在解决方案资源管理器中,右键单击项目,选择“添加”>“新建项”。
  3. 在“添加新项”对话框中,选择“数据”类别,然后选择“LINQ to SQL 类”模板。
  4. 给 Linq to SQL 类命名并点击“添加”按钮。
  5. 在 Server Explorer 中连接到数据库。
  6. 在“服务器资源”窗格中,展开数据库,然后将表拖动到 Linq to SQL 类设计器中,以创建对应的实体类和 DataContext 类。

如果您仍然无法找到 Linq to SQL 类,请确保已安装 Linq to SQL 组件。在 Visual Studio 2019 中,您可以通过转到“工具”>“获取工具和功能”>“修改”>“单个组件”>“SDK、库和框架”>“.NET 框架 4.x 开发人员工具”>“Linq to SQL 工具”来安装 Linq to SQL 组件。【直接搜索相应的工具名即可】

LINQ to SQL Classes数据工具

“LINQ to SQL Classes”是一种ORM(对象关系映射)工具,它可用来生成针对SQL Server数据库的数据访问代码。下面是如何使用Visual Studio中的这个功能来创建LINQ to SQL Classes:

  1. 打开Visual Studio并创建一个新的项目(或打开现有的项目)。
  2. 解决方案资源管理器中,右键点击您的项目并选择添加 > 新建项
  3. 在弹出的“添加新项”对话框中选择数据,然后选择LINQ to SQL Classes,为其输入一个合适的名字如“DataClasses.dbml”并单击添加
  4. 在打开的“Object Relational Designer”窗口,您可以直接从服务器资源管理器中拖拽数据库表、视图或存储过程到设计面板中。
  5. 每一个拖入设计面板中的表将生成一个对应名称的class,并且该表中的每个字段将会在class中变成一个property。
  6. 完成设计后,保存该文件。Visual Studio会为您创建一组对应的.cs代码文件,这些就是您的数据访问代码。

LINQ To Sql 步骤

LINQ to SQL 是一种在.NET Framework中使用LINQ查询关系数据库的技术。它通过将数据库架构映射到对象模型来简化数据库的访问操作。

即: ORM(Object Relation Mapping.

下面是使用 LINQ to SQL 的一般步骤和示例:

  1. 创建数据库表结构:在关系数据库中创建并定义操作的表和字段。
  2. 引用 LINQ 命名空间:在代码文件的顶部,使用 using 关键字引用 System.Linq 命名空间,以便使用 LINQ 相关的类型和扩展方法。
  3. 连接到数据库:添加一个数据连接,连接到目标数据库。
  4. 创建对象模型:使用 Visual Studio 工具中的 “LINQ to SQL Classes”,从连接的数据库中提取架构信息,自动生成与数据库表对应的类和属性。
  5. 构建查询:利用生成的对象模型,使用 LINQ 查询表达式或扩展方法语法构建查询。例如,可以使用 fromwhereselect 等关键字来过滤和选择数据。
  6. 执行查询和操作:通过对查询结果执行相关操作,例如排序、更新、插入或删除数据。

在代码中,您可以创建出LINQ to SQL Classes的实例,并使用LINQ语法查询数据库。

下面是一个简单的 LINQ to SQL 示例,演示如何查询数据库中的数据:

using System;
using System.Linq;//引入

public class Program
{
    public static void Main()
    {
        // 创建 LINQ to SQL 数据上下文
        // CRUD都找这个上下文对象
        using (var db = new MyDataContext())
        {
            // 构建查询
            var query = from c in db.Customers
                        where c.City == "London"
                        select c;

            // 执行查询并处理结果
            foreach (var customer in query)
            {
                Console.WriteLine(customer.CustomerName);
            }
        }
    }
}

//在此示例中,我们首先创建了一个 `MyDataContext` 的 LINQ to SQL 数据上下文对象,该对象表示与数据库的连接。然后,我们构建了一个查询,查询 `Customers` 表中城市为 “London” 的客户名。最后,我们通过循环打印出查询结果中的客户名。

//通过以上步骤,你可以开始使用 LINQ to SQL 来访问和操作关系数据库,以更简洁和直观的方式进行数据查询。

这就是如何使用Visual Studio中的“LINQ to SQL Classes”的功能。在上述步骤中,首先在项目中添加一个LINQ to SQL classes项,然后向该设计面板中拖拽数据库对象创建相应的数据类。完成后,你就可以在代码中使用这些类和LINQ语法进行数据库查询。

Entity Framework(EF)

如果要操作Mysql数据库,使用第三方库如Entity Framewor,还是可以实现.Net环境下对MySQL数据库的访问和操作。

Entity Framework(EF)是一款由Microsoft支持的开源对象关系映射(ORM)框架,它允许.NET开发人员在使用数据库时更加专注于业务逻辑,减少输入大量SQL代码的工作量。

以下是使用Entity Framework配合MySQL进行操作的基本步骤:

  1. 打开Visual Studio,创建或打开已有项目。
  2. 打开工具 -> NuGet包管理器 -> 管理解决方案的NuGet程序包
  3. 在打开的NuGet包管理器中搜索MySql.Data.EntityFramework并安装。
  4. 在你的模型类上添加[System.Data.Linq.Table(Name = “your_table_name”)]特性,你的列的属性添加[System.Data.Linq.Column(IsPrimaryKey = true, IsDbGenerated = true)]特性。
  5. 创建你的数据上下文类继承自DbContext,并且创建对应的DbSet属性。

以上就是一种方式来连接MySQL和EF,另外你还可以通过Code FirstDatabase First或者Model First来创建模型。

然而,如果你还是想用类似LINQ to SQL的方式来操作MySQL数据库,可以选择Dapper, NHibernate等其它比较流行的ORM框架。

winform项目打包exe

在Visual Studio 2019中,创建WinForms应用的exe文件实际上是在编译过程中自动进行的。

编译成功后,exe文件位于项目的bin\Debug或bin\Release目录下(取决于的编译输出路径的配置)。

以下是将WinForms项目打包为exe程序并供其他计算机使用的步骤:

  1. 在编写代码并测试完成后,确保你处在要打包的解决方案中。
  2. 点击vs菜单栏中的生成菜单选项,然后选择生成解决方案。这将开始Compile或包含在解决方案中的所有项目。确保编译选项是Release,而不是Debug,因为Release版本可以达到更好的性能。
  3. 编译完成后,找到你项目的输出目录。默认的输出目录通常在你的项目文件夹内的bin\Release子文件夹。
  4. 在目标计算机上,只需将这些所需的文件和文件夹复制过去(打成压缩包),然后解压运行其中的exe文件即可。

注意:目标机器上需要安装正确版本的.NET Framework才能运行你的应用程序。运行exe文件前,确保已经安装了所需的.NET Framework。

如果没有,你需要提供.NET Framework的安装程序或引导用户去下载安装。如果使用了.NET Core(现在是.NET 5及更新版本),可以选择发布为自包含的应用,这样无需单独安装.NET运行时环境。

创作不易!!欢迎关注个人公众号!!获取更多知识内容!!

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
社会演员多的头像社会演员多普通用户
上一篇 2023年12月20日
下一篇 2023年12月20日

相关推荐