WPF/C#:理解与实现WPF中的MVVM模式

MVVM模式的介绍

MVVM(Model-View-ViewModel)是一种设计模式,特别适用于WPF(Windows Presentation Foundation)等XAML-based的应用程序开发。MVVM模式主要包含三个部分:Model(模型)、View(视图)和ViewModel(视图模型)。

  1. Model(模型):模型代表的是业务逻辑和数据。它包含了应用程序中用于处理的核心数据对象。模型通常包含业务规则、数据访问和存储逻辑。
  2. View(视图):视图是用户看到和与之交互的界面。在WPF中,视图通常由XAML定义,并且包含各种用户界面元素,如按钮、文本框、列表等。
  3. ViewModel(视图模型):视图模型是视图的抽象,它包含视图所需的所有数据和命令。视图模型通过实现INotifyPropertyChanged接口和使用ICommand对象,将视图的状态和行为抽象化,从而实现了视图和模型的解耦。

MVVM模式的主要优点是分离了视图和模型,使得视图和业务逻辑之间的依赖性降低,提高了代码的可维护性和可测试性。此外,通过数据绑定和命令绑定,MVVM模式可以减少大量的样板代码,使得代码更加简洁和易于理解。

image-20240527095704838

不使用MVVM模式的例子

要真正理解为什么要使用MVVM,使用MVVM有什么好处,肯定要与不使用MVVM的情况进行对比。在Winform中我们使用了事件驱动编程,同样在WPF中我们也可以使用事件驱动编程。

Windows Forms(WinForms)是一种基于事件驱动的图形用户界面(GUI)框架。在WinForms中,用户与应用程序的交互主要通过事件来驱动。

事件驱动编程是一种编程范式,其中程序的执行流程由外部事件(如用户操作或系统事件)决定。在WinForms中,事件可以是用户的各种操作,如点击按钮、选择菜单项、输入文本等,也可以是系统的事件,如窗口加载、大小改变等。

当一个事件发生时,会触发与之关联的事件处理器(Event Handler)。事件处理器是一个函数或方法,用于响应特定的事件。例如,当用户点击一个按钮时,可以触发一个事件处理器,执行一些特定的操作。

在WinForms中,你可以为各种控件添加事件处理器,以响应用户的操作。这种事件驱动的方式使得你可以很容易地创建交互式的GUI应用程序,而无需关心程序的执行流程。

事件驱动的简图如下图所示:

image-20240527100814935

  1. 事件源(Event Source):事件源是产生事件的对象。在WinForms中,事件源通常是用户界面元素,如按钮、文本框、菜单项等。当用户与这些元素进行交互(如点击按钮、输入文本等)时,这些元素就会产生相应的事件。
  2. 事件(Event):事件是由事件源产生的一个信号,表示某种特定的事情已经发生。例如,当用户点击一个按钮时,按钮就会产生一个Click事件。事件通常包含一些关于该事件的信息,例如事件发生的时间、事件的源对象等。
  3. 事件处理器(Event Handler):事件处理器是一个函数或方法,用于响应特定的事件。当一个事件发生时,与该事件关联的事件处理器就会被调用。在事件处理器中,你可以编写代码来定义当事件发生时应该执行的操作。例如,你可以在按钮的Click事件处理器中编写代码,定义当用户点击按钮时应该执行的操作。

现在我们通过一个例子在WPF中使用事件驱动编程。

首先看一下我们的示例xaml页面:

 <Window x:Class="WPF_MVVM_Pattern.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:WPF_MVVM_Pattern"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        Loaded="Window_Loaded">
    <StackPanel>
        <ToolBar>
            <Label Content="姓名:"></Label>
            <TextBox x:Name="nameTextBox" Width="50"></TextBox>
            <Label Content="邮箱:"></Label>
            <TextBox x:Name="emailTextBox" Width="100"></TextBox>
            <Button Content="添加"
                    Click="AddUser"></Button>
       </ToolBar>
        <StackPanel>
            <DataGrid x:Name="dataGrid1"></DataGrid>
            
        </StackPanel>
    </StackPanel>
</Window>

image-20240527141918081

使用了两个事件,分别是窗体加载事件:

 Loaded="Window_Loaded"

与button点击事件:

<Button Content="添加"
        Click="AddUser"></Button>

实现该操作与两个类有关:

 public class User
 {
     public string? Name { get; set; }
     public string? Email { get; set; }
 }
 public static class UserManager
 {
     public static ObservableCollection<User> DataBaseUsers = new       ObservableCollection<User>()
     {
         new User() { Name = "小王", Email = "123@qq.com" },
         new User() { Name = "小红", Email = "456@qq.com" },
         new User() { Name = "小五", Email = "789@qq.com" }
     };

     public static ObservableCollection<User> GetUsers()
     {
         return DataBaseUsers;
     }

     public static void AddUser(User user)
     {
         DataBaseUsers.Add(user);
     }
 }

窗体加载事件处理程序:

 private void Window_Loaded(object sender, RoutedEventArgs e)
 {
    dataGrid1.ItemsSource =  UserManager.GetUsers();
 }

"添加"按钮点击事件处理程序:

 private void AddUser(object sender, RoutedEventArgs e)
 {
     User user = new User();
     user.Name = nameTextBox.Text;
     user.Email = emailTextBox.Text;
     UserManager.AddUser(user);
     MessageBox.Show("成功添加用户!");
 }

实现的效果如下所示:

使用MVVM的例子

刚刚我们使用的是事件驱动编程,我们在winform开发中经常这样干。对于一些小项目或者demo程序这样做很方便,但是如果业务逻辑很多,这样做就不好维护,因为UI与业务逻辑严重耦合了。

我们经常在cs文件中使用xaml中的元素,也就是经常在cs中引用xaml中的元素,如下所示:

image-20240527155929453

在C#代码文件中直接引用XAML元素,会导致代码与界面元素之间的耦合度增加,这是一种不良的编程实践。以下是这种做法的一些潜在问题:

  1. 耦合度高:代码与界面元素紧密耦合,这使得代码更难以维护和重用。如果你更改了界面元素(例如更改了元素的名称或类型),你可能需要修改引用这个元素的所有代码。
  2. 测试困难:由于代码直接依赖于界面元素,这使得单元测试变得困难。你可能需要创建一个界面元素的实例,或者使用复杂的模拟技术,才能测试这些代码。
  3. 违反MVVM模式:在WPF中,推荐使用MVVM(Model-View-ViewModel)模式来组织代码。在MVVM模式中,视图(View)和模型(Model)之间的交互是通过视图模型(ViewModel)来进行的,而不是直接在代码中引用界面元素。

开始使用MVVM模式

RelayCommand

首先新建一个Commands文件夹,新建一个RelayComand类:

image-20240528084137467

RelayCommand如下:

public class RelayCommand : ICommand
{
  
    public event EventHandler? CanExecuteChanged;

    private Action<object> _Excute { get; set; }

    private Predicate<object> _CanExcute { get;set; }

    public RelayCommand(Action<object> ExcuteMethod, Predicate<object> CanExcuteMethod)
    {
        _Excute = ExcuteMethod;
        _CanExcute = CanExcuteMethod;
    }

    public bool CanExecute(object? parameter)
    {
       return _CanExcute(parameter);
    }

    public void Execute(object? parameter)
    {
       _Excute(parameter);
    }
}

RelayCommand实现了ICommand接口。

先来介绍一下ICommand接口。

ICommand

在WPF(Windows Presentation Foundation)中,ICommand是一个接口,它定义了一种机制,用于在用户界面(UI)中处理事件,这种机制与用户界面的具体行为进行了解耦。这是实现MVVM(Model-View-ViewModel)设计模式的关键部分。

ICommand接口包含两个方法和一个事件:

  • Execute(object parameter):当调用此命令时,应执行的操作。
  • CanExecute(object parameter):如果可以执行Execute方法,则返回true;否则返回false。这可以用于启用或禁用控件,例如按钮。
  • CanExecuteChanged事件:当CanExecute的返回值可能发生更改时,应引发此事件。

ICommand的结构图如下所示:

image-20240528084534354

代码如下所示:

public interface ICommand
 {
  
     event EventHandler? CanExecuteChanged;
  
     bool CanExecute(object? parameter);
    
     void Execute(object? parameter);
 }

现在再来看看RelayCommand

RelayCommand

RelayCommand是一种常用于WPF和MVVM模式的设计模式,它是一种特殊的命令类型。在MVVM模式中,RelayCommand允许将命令的处理逻辑从视图模型中分离出来,使得视图模型不需要知道命令的具体执行逻辑,从而实现了视图模型和命令处理逻辑的解耦。

RelayCommand通常包含两个主要部分:CanExecuteExecuteCanExecute是一个返回布尔值的函数,用于确定命令是否可以执行。Execute是一个执行命令的函数,当CanExecute返回true时,Execute将被调用。

这种设计模式使得你可以在不改变视图模型的情况下,更改命令的处理逻辑,提高了代码的可维护性和可重用性。

简单理解就是RelayCommandICommand接口的一个常见实现,它允许你将ExecuteCanExecute的逻辑定义为委托,从而实现对命令的灵活处理。

在RelayCommand中我们定义了两个委托:

private Action<object> _Excute { get; set; }

private Predicate<object> _CanExcute { get;set; }

Action<object>是一个委托,它封装了一个接受单个参数并且没有返回值的方法。这个参数的类型是object

对应于这一部分:

image-20240528085200268

Predicate<object>是一个委托,它封装了一个接受单个参数并返回一个bool值的方法。这个参数的类型是object

对应于这一部分:

image-20240528085237548

RelayCommand的构造函数为:

 public RelayCommand(Action<object> ExcuteMethod, Predicate<object> CanExcuteMethod)
 {
     _Excute = ExcuteMethod;
     _CanExcute = CanExcuteMethod;
 }

现在去看看View—ViewModel

View—ViewModel

ViewModel是一个抽象,它代表了View的状态和行为。ViewModel包含了View所需的数据,并提供了命令以响应View上的用户操作。ViewModel不知道View的具体实现,它只知道如何提供View所需的状态和行为。

ViewModel的主要职责包括:

  • 数据绑定:ViewModel提供了View所需的数据。这些数据通常是以属性的形式提供的,当这些属性的值改变时,ViewModel会通过实现INotifyPropertyChanged接口来通知View。
  • 命令绑定:ViewModel提供了命令以响应View上的用户操作。这些命令通常是以ICommand接口的实现的形式提供的。
  • 视图逻辑:ViewModel包含了View的逻辑,例如,决定何时显示或隐藏某个元素,何时启用或禁用某个按钮等。

新建一个ViewModel文件夹,在该文件夹中新建一个MainViewModel类:

image-20240528093058019

目前写的MainViewModel如下:

public class MainViewModel
{
    public ObservableCollection<User> Users { get; set; }
    public ICommand AddUserCommand { get; set; }
    public string? Name { get; set; }
    public string? Email { get; set; }

    public MainViewModel()
    {
        Users = UserManager.GetUsers();
        AddUserCommand = new RelayCommand(AddUser, CanAddUser);
    }

    private bool CanAddUser(object obj)
    {
        return true;
    }

    private void AddUser(object obj)
    {
        User user = new User();
        user.Name = Name;
        user.Email = Email;
        UserManager.AddUser(user);
    }
}

现在我们结合这张图,理解View与ViewModel之间的关系:

image-20240528093349406

一个一个来理解,首先最重要的就是数据绑定。

现在View的xaml如下:

<Window x:Class="WPF_MVVM_Pattern.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:WPF_MVVM_Pattern"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel>
        <ToolBar>
            <Label Content="姓名:"></Label>
            <TextBox Text="{Binding Name}"  Width="50"></TextBox>
            <Label Content="邮箱:"></Label>
            <TextBox Text="{Binding Email}" Width="100"></TextBox>
            <Button Content="添加"
                    Command="{Binding AddUserCommand }"></Button>          
        </ToolBar>
        <StackPanel>
            <DataGrid ItemsSource="{Binding Users}"></DataGrid>
            
        </StackPanel>
    </StackPanel>
</Window>

cs如下:

public partial class MainWindow : Window
{      
    public MainWindow()
    {
        InitializeComponent();
        MainViewModel mainViewModel = new MainViewModel();
        this.DataContext = mainViewModel;
    }
   
}

将MainWindow的DataContext赋值给了mainViewModel。

 <TextBox Text="{Binding Name}"  Width="50"></TextBox>
 <TextBox Text="{Binding Email}" Width="100"></TextBox>
 <DataGrid ItemsSource="{Binding Users}"></DataGrid>

中进行了数据绑定,对应于图中的这一部分:

image-20240528094205054

现在来看看命令绑定。

 <Button Content="添加"
         Command="{Binding AddUserCommand }"></Button>  

进行了命令绑定,对应于图中这一部分:

image-20240528094348003

现在先来看看效果:

实现的效果

现在已经实现了与前面基于事件驱动同样的效果,但是上面那张图中的Send Notifications还没有体现。

Send Notifications表示ViewModel中的更改会通知View。

现在我们来以一个例子说明一下Send Notifications是如何实现的。

首先添加一个测试命令:

 public ICommand TestCommand { get; set; }

在构造函数中添加:

 TestCommand = new RelayCommand(Test, CanTest);

实现Test与CanTest方法:

private bool CanTest(object obj)
{
    return true;
}

private void Test(object obj)
{
    Name = "小1";
    Email = "111@qq.com";
}

View中修改如下:

 <Button Content="测试"
         Command="{Binding TestCommand }"></Button>

现在去尝试,我们会发现没有效果,原因是我们的ViewModel没有实现INotifyPropertyChanged接口。

INotifyPropertyChanged接口介绍

在WPF(Windows Presentation Foundation)中,INotifyPropertyChanged接口用于实现数据绑定中的属性更改通知。当绑定到UI元素的数据源中的属性值发生更改时,INotifyPropertyChanged接口可以通知UI元素更新。

INotifyPropertyChanged接口只定义了一个事件:PropertyChanged。当属性值发生更改时,应触发此事件。事件参数PropertyChangedEventArgs包含更改的属性的名称。

现在我们的MainViewModel实现一下INotifyPropertyChanged接口,如下所示:

 public class MainViewModel : INotifyPropertyChanged
 {
     public ObservableCollection<User> Users { get; set; }
     public ICommand AddUserCommand { get; set; }
     public ICommand TestCommand { get; set; }

     private string? _name;
     public string? Name
     {
         get { return _name; }
         set
         {
             if (_name != value)
             {
                 _name = value;
                 OnPropertyChanged(nameof(Name));
             }
         }
     }

     private string? _email;
     public string? Email
     {
         get { return _email; }
         set
         {
             if (_email != value)
             {
                 _email = value;
                 OnPropertyChanged(nameof(Email));
             }
         }
     }

     public MainViewModel()
     {
         Users = UserManager.GetUsers();
         AddUserCommand = new RelayCommand(AddUser, CanAddUser);
         TestCommand = new RelayCommand(Test, CanTest);
     }

     private bool CanTest(object obj)
     {
         return true;
     }

     private void Test(object obj)
     {
         Name = "小1";
         Email = "111@qq.com";
     }

     private bool CanAddUser(object obj)
     {
         return true;
     }

     private void AddUser(object obj)
     {
         User user = new User();
         user.Name = Name;
         user.Email = Email;
         UserManager.AddUser(user);
     }

     public event PropertyChangedEventHandler? PropertyChanged;

     protected virtual void OnPropertyChanged(string propertyName)
     {
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
     }
 }

现在再尝试一下,会发现ViewModel中的更改会成功通知View了,如下所示:

对应于图中的这一部分:

image-20240528101649982

现在我们来看看ViewModel—Model。

ViewModel—Model

现在我们来看看ViewModel与Model之间的关系,可以根据下面这张图进行理解:

image-20240528101926207

Model(模型):Model代表了业务逻辑和数据。它包含了应用程序中的数据和对数据的操作,例如,从数据库中获取数据,或者向数据库中添加数据。Model是独立于UI的,它不知道UI的存在。

ViewModel(视图模型):ViewModel是Model和View之间的桥梁。它包含了View所需的数据(这些数据来自于Model),并提供了命令以响应View上的用户操作。ViewModel将Model的数据转换为View可以显示的数据,同时,它也将View上的用户操作转换为对Model的操作。

在我们这个例子中我们的数据来源于Model文件夹下的User类与UserManager类:

image-20240528103959230

这里的Send Notifications又该如何理解呢?

我们也是以一个小例子进行说明。

首先将ViewModel中的Test方法修改为:

 private void Test(object obj)
 {
     Users[0].Name = "小1";
     Users[0].Email = "111@qq.com";
 }

会发现现在并不会发送通知,实现View上的修改,这是因为User类并没有实现INotifyPropertyChanged接口,现在修改User类实现INotifyPropertyChanged接口:

public class User : INotifyPropertyChanged
{
    private string? _name;
    public string? Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
    }

    private string? _email;
    public string? Email
    {
        get { return _email; }
        set
        {
            if (_email != value)
            {
                _email = value;
                OnPropertyChanged(nameof(Email));
            }
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

现在可以实现通知了,效果如下所示:

使用MVVM库

我们会发现如果全部都手动实现MVVM模式的话,代码有点多,有点麻烦。这时候就可以使用一些MVVM库来简化我们的操作。

这里以CommunityToolkit.Mvvm为例,进行说明。

CommunityToolkit.Mvvm介绍

CommunityToolkit.Mvvm是Microsoft Community Toolkit的一部分,它是一个轻量级但功能强大的MVVM(Model-View-ViewModel)库,旨在帮助开发者更容易地实现MVVM设计模式。

该库提供了一些基础类,如ObservableObjectObservableRecipient,这些类实现了INotifyPropertyChanged接口,并提供了SetProperty方法,可以在属性值改变时触发PropertyChanged事件。这使得数据绑定变得更加简单和高效。

此外,该库还提供了ICommand接口的实现,如RelayCommandAsyncRelayCommand,这些类可以帮助你创建命令,命令是MVVM模式中的一个重要组成部分。

CommunityToolkit.Mvvm还提供了一些其他有用的特性,如消息传递、设计时数据支持等,这些特性可以帮助你更好地组织和管理你的代码。

CommunityToolkit.Mvvm是一个强大的工具,它可以帮助你更容易地实现MVVM模式,从而提高你的代码质量和开发效率。

image-20240528112612211

修改之后的ViewModel如下所示:

 public partial class MainViewModel : ObservableObject
 {
     public ObservableCollection<User> Users { get; set; }    

     [ObservableProperty]
     private string? name;

     [ObservableProperty]
     private string? email;
    

     public MainViewModel()
     {
         Users = UserManager.GetUsers();         
     }
  
     [RelayCommand]
     private void Test(object obj)
     {
         Users[0].Name = "小1";
         Users[0].Email = "111@qq.com";
     }
    
     [RelayCommand]
     private void AddUser(object obj)
     {
         User user = new User();
         user.Name = Name;
         user.Email = Email;
         UserManager.AddUser(user);
     }
    
 }

修改之后的User类如下所示:

 public partial class User : ObservableObject
 {
     [ObservableProperty]
     private string? _name;

     [ObservableProperty]
     private string? _email;            
 }

用到了CommunityToolkit.Mvvm库中的三个东西,分别是ObservableObject、[ObservableProperty]与[RelayCommand]。

先来看一下ObservableObject。

ObservableObjectCommunityToolkit.Mvvm库中的一个基础类,它实现了INotifyPropertyChanged接口。这个接口是.NET数据绑定基础架构的一部分,当对象的一个属性改变时,它会通知绑定到该属性的任何元素。

image-20240528120932293

image-20240528121004023

具体见:ObservableObject - Community Toolkits for .NET | Microsoft Learn

在这里我们使用

 [ObservableProperty]
 private string? name;

它将生成一个像这样的可观察属性:

public string? Name
{
    get => name;
    set => SetProperty(ref name, value);
}

具体见:ObservableProperty attribute - Community Toolkits for .NET | Microsoft Learn

我们使用

[RelayCommand]
private void AddUser(object obj)
{
   User user = new User();
   user.Name = Name;
   user.Email = Email;
   UserManager.AddUser(user);
}

代码生成器会生成一个命令如下所示:

private RelayCommand? addUserCommand;

public IRelayCommand AddUserCommand => addUserCommand ??= new RelayCommand(AddUser);

具体见:RelayCommand attribute - Community Toolkits for .NET | Microsoft Learn

现在我们的ViewModel与Model就可以简化了,现在再来看看效果:

总结

本文先总体介绍了一下MVVM模式,关于MVVM模式可以根据这张图帮助理解:

image-20240527095704838

由于很多同学可能与我一样,是从winform到wpf的,因此在wpf中使用winform中的事件驱动编程范式完成了一个小例子,关于事件驱动编程,可以根据这张图帮助理解:

image-20240527100814935

由于这种模式耦合比较多,我们想要松耦合,因此开始学习MVVM模式。我们创建了实现ICommand接口的RelayCommand类,实现INotifyPropertyChanged接口的MainViewModel类与User类。使用数据绑定与命令绑定改写xaml页面。

最后由于手动实现MVVM模式,需要写很多代码,看过去比较复杂与麻烦,我们可以使用MVVM库来简化MVVM模式的实现。

以上,就是本次分享的全部内容,希望对正在学习wpf中mvvm模式的同学有所帮助,如果有什么不对的地方,恳请批评指正,共同进步!

参考

1、What is the MVVM pattern, What benefits does MVVM have? (youtube.com)

2、WPF MVVM Tutorial: Build An App with Data Binding and Commands (youtube.com)

3、Model-View-ViewModel - .NET | Microsoft Learn

4、Introduction to the MVVM Toolkit - Community Toolkits for .NET | Microsoft Learn

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

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

相关文章

02 Prometheus入门安装教程

02 Prometheus入门安装教程 大家好&#xff0c;我是秋意零。今天分享一篇入门级Prometheus安装教程。 环境准备 三台Linux虚拟机&#xff08;一台也可以&#xff09; 准备Prometheus、相关组件安装包 Prometheus官网下载安装包比较慢&#xff0c;如果没有魔法。可关注公众号…

YOLOv8猫狗检测:从SwanLab可视化训练到Gradio Demo网站

基于YOLO模型在自定义数据上做训练&#xff0c;实现对特定目标的识别和检测&#xff0c;是CV领域非常经典的任务&#xff0c;也是AI项目落地最热门的方向之一。 这篇文章我将带大家使用Ultralytics、SwanLab、Gradio这两个开源工具&#xff0c;完成从数据集准备、代码编写、可…

小红书推流机制底层逻辑

小红书推流机制底层逻辑 很多做运营的朋友问小红薯怎么玩❓ 小红书的核心逻辑流量是不是玄学❓ 今天就来说说小红书的流量算法机制&#x1f525; ①电脑审核 ②分配初始流量 ③增加流量 ④推荐结束

MyBatis系统学习篇 - MyBatis逆向工程

MyBatis的逆向工程是指根据数据库表结构自动生成对应的Java实体类、Mapper接口和XML映射文件的过程。逆向工程可以帮助开发人员快速生成与数据库表对应的代码&#xff0c;减少手动编写重复代码的工作量。 我们在MyBatis中通过逆向工具来帮我简化繁琐的搭建框架&#xff0c;减少…

【UML】-01-UML基本元素的介绍

1、UML的词汇表 &#xff08;1&#xff09;事物&#xff1b; &#xff08;2&#xff09;关系&#xff1b; &#xff08;3&#xff09;图。 事物是对模型中首要成分的抽象&#xff1b;关系把事物结合在一起&#xff1b;图聚集了相关的事物。 注&#xff1a;事物也称为元素 2…

LabVIEW高低温试验箱控制系统

要实现LabVIEW高低温试验箱控制系统&#xff0c;需要进行硬件配置、软件设计和系统集成&#xff0c;确保LabVIEW能够有效地监控和控制试验箱的温度。以下是详细说明&#xff1a; 硬件配置 选择合适的试验箱&#xff1a; 确定高低温试验箱的型号和品牌。 确认试验箱是否支持外…

蓝桥杯嵌入式国赛笔记(3):其他拓展板程序设计(温、湿度传感器、光敏电阻等)

目录 1、DS18B20读取 2、DHT11 2.1 宏定义 2.2 延时 2.3 设置引脚输出 2.4 设置引脚输入 2.5 复位 2.6 检测函数 2.7 读取DHT11一个位 2.7.1 数据位为0的电平信号显示 2.7.2 数据位为1的电平信号显示 2.8 读取DHT11一个字节 2.9 DHT11初始化 2.10 读取D…

2024.5.28晚训题解

提前预告&#xff0c;市赛初中组会考算法题&#xff0c;应该会有两道模板题 比如DFS BFS 二分 简单动态规划&#xff0c;虽然我们没学多久&#xff0c;但是模板题你还是要会写的 A题 编辑距离 动态规划 注意多组输入 #include<iostream> using namespace std; int dp[1…

2024最新升级Stable Diffusion整合包v4.6版来了,附赠SD电商实战教程

Stable Diffusion无疑是最近最火的AI绘画工具之一&#xff0c;本期设计软件库给大家带来了2024最新升级的v4.6版&#xff01;比之前推送的更加智能、快速和简单 2024全新Stable Diffusion 资料包 新版本使用更方便 独家附赠SD电商实战教程 让你快速上手 资源目录一览 01 新…

数据通信基本概念汇总

1. 数据通信基础 网关: 提供协议转换&#xff0c;路由选择&#xff0c;数据交换的网络设备 报文: 网络中所传递的一个数据单元。 数据载荷: 最终要传递的信息 封装: 给数据载荷添加头部和尾部的过程(形成新的报文) 解封装: 给数据载荷去掉头部和尾部的过程(获取数据载荷) 终端设…

[XYCTF新生赛]-Reverse:你是真的大学生吗?解析(汇编异或逆向)

无壳 查看ida 没有办法反汇编&#xff0c;只能直接看汇编了。 这里提示有输入&#xff0c;输入到2F地址后&#xff0c;然后从后往前异或&#xff0c;其中先最后一个字符与第一个字符异或。这里其实也有字符串的长度&#xff0c;推测应该是cx自身异或之后传给了cx 完整exp&am…

【Go语言入门学习笔记】Part3.指针和运算符、以及基本输入

一、前言 仍然好多和C语言类似&#xff0c;计算机的学生应该是很容易入门这一环节&#xff0c;我还在最后的输入中看到了一些些Java输入的影子&#xff0c;而自动的变量类型推断更是有Python那个味道&#xff0c;正可谓几百家之所长了。 二、学习代码 package mainimport (&q…

AI答题项目,无门槛答题一小时收益30+

朋友们&#xff0c;今天我想和大家探讨一个令人兴奋的副业机遇。你是否曾感觉到日常工作的枯燥乏味&#xff0c;而又渴望找到一种轻松的赚钱方式来增加你的收入&#xff1f;今天我将和你分享的这个项目正是你所期待的。 项目的核心是利用AI技术来回答网上付费用户的问题&…

selenium源码学习

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

蓝桥楼赛第30期-Python-第三天赛题 提取用户输入信息题解

楼赛 第30期 Python 模块大比拼 提取用户输入信息 介绍 正则表达式&#xff08;英文为 Regular Expression&#xff0c;常简写为regex、regexp 或 RE&#xff09;&#xff0c;也叫规则表达式、正规表达式&#xff0c;是计算机科学的一个概念。 所谓“正则”&#xff0c;可以…

order by工作过程和优化

工作过程 order by 是由优化器决定的&#xff0c;如果优化器认为filesort速度快&#xff0c;那么走filesort排序&#xff0c;如果优化器认为索引速度快&#xff0c;那么走索引排序。

【云原生_K8S系列】认识 Kubernetes

在当今数字化转型的浪潮中&#xff0c;企业对于构建高效、灵活的软件架构有了更高的期望。而在这个迅速变化的环境中&#xff0c;容器化技术如雨后春笋般涌现&#xff0c;为解决传统部署和管理软件所带来的挑战提供了一种全新的解决方案。在众多容器编排工具中&#xff0c;Kube…

JavaScript--作用域是什么

作用域是什么 编译原理 在传统的编译语言中&#xff0c;程序中的一段源代码在执行之前会经历三个步骤。成为编译 分词/词法分析 这个过程由字符组成的字符串分解成有意义的代码块&#xff0c;这些代码块成为词法单元。 分词和词法分析之间的主要差异在于词法单元的识别是有…

【网络协议】应用层协议HTTPS

文章目录 为什么引入HTTPS&#xff1f;基本概念加密的基本过程对称加密非对称加密中间人攻击证书 为什么引入HTTPS&#xff1f; 由于HTTP协议在网络传输中是明文传输的&#xff0c;那么当传输一些机密的文件或着对钱的操作时&#xff0c;就会有泄密的风险&#xff0c;从而引入…

项目构建工具maven

一、概述 1、maven是apache的一个开源项目&#xff0c;是一个优秀的项目构建/管理工具 2、apache(软件基金会、非盈利组织、管理维护一些开源项目) 二、功能 1、管理项目中jar包和jar包与jar包之间的依赖 2、完成项目编译、测试、打包 三、核心文件 pom.xml:在里面配置相…