WPF MVVM框架

news2024/11/19 10:40:14

一、MVVM简介

MVC Model View Control

MVP

MVVM即Model-View-ViewModel,MVVM模式与MVP(Model-View-Presenter)模式相似,主要目的是分离视图(View)和模型(Model),具有低耦合、可重用性、独立开发、可测试性等优点。

MVVM框架有很多,开源的主要有:

  • PRISM:由微软提供,和MEF/Unity一起用于依赖注入,支持组合命令,可以扩展。MSDN上有详细的教程和演练。

  • MVVM Light Toolkit:有visual Studio和Expression Blend的项目和项的模板。更多信息请看这里,另外可以参考VS和Expression Blend的使用教程。

  • Caliburn Micro:支持视图模型先行(ViewModel-First)和视图先行(View-First)两种开发方式,通过co-routine支持异步编程。

  • Simple MVVM Toolkit:提供VS项目和项的模板,依赖注入,支持深拷贝以及模型和视图模型之间的属性关联。

  • Catel:包含项目和项的模板,用户控件和企业类库。支持动态视图模型注入,视图模型的延迟加载和验证。还支持WP7专用的视图模型服务。

闭源框架主要有:

  • Intersoft ClientUI:付费的,只支持WPF和Silverlight,但是,除了MVVM框架,它还提供其它一些特性。

  • Vidyano:免费但不开源。带有实体映射/虚拟持久化对象(数据容器),业务规则以及内置基于ACL的安全特性。

二、原生版本的MVVM实例

Models 存放的是数据模型

Service存放的是业务逻辑

ViewModels存放的便是视图模型

Views存放WPF窗口

2.1 Models文件夹中创建一个用户模型User.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
​
namespace Wpfmvvm_demo.Models
{
    public class User
    {
        /// <summary>
        /// 用户名
        /// </summary>
        public string? Name { get; set; }
        /// <summary>
        /// 密码
        /// </summary>
        public string? Password { get; set; }
    }
}

2.2 在Services文件夹中添加用户的业务逻辑UserService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wpfmvvm_demo.Models;
​
namespace Wpfmvvm_demo.Services
{
    public class UserService
    {
        /// <summary>
        /// 获取所有用户方法
        /// </summary>
        /// <returns></returns>
        public List<User> GetAllUser()
        {
            List<User> users = new ();
​
            for (int i = 0; i < 3; i++)
            {
                var user = new User();
                user.Name = "用户" + i;
                user.Password = "密码" + i;
                users.Add(user);
            }
​
            return users;
        }
    }
}

2.3 在ViewModels中创建NotificationObject.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
​
namespace Wpfmvvm_demo.ViewModels
{
    public class NotificationObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;
​
        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

2.4 在ViewModels文件中创建DelegateCommand.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
​
namespace Wpfmvvm_demo.ViewModels
{
    public class DelegateCommand : ICommand
    {
        public event EventHandler? CanExecuteChanged;
        public Func<object?,bool>? DoCanExecute { get; set; }
        public Action<object?>? DoExecute { get; set; }
​
        public bool CanExecute(object? parameter)
        {
            if (DoCanExecute != null)
            {
                return DoCanExecute(parameter);
            }
            return true;
        }
​
        public void Execute(object? parameter)
        {
            if (DoExecute != null)
            {
                DoExecute.Invoke(parameter);
            }
        }
    }
}

2.5 ViewModels中创建主窗口的视图模型MainWindowViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wpfmvvm_demo.Models;
using Wpfmvvm_demo.Services;
​
namespace Wpfmvvm_demo.ViewModels
{
    public class MainWindowViewModel : NotificationObject
    {
        /// <summary>
        /// 用户List
        /// </summary>
        private List<User>? users;
        public List<User>? Users
        {
            get { return users; }
            set
            {
                users = value;
                RaisePropertyChanged("Users");
            }
        }
​
        /// <summary>
        /// 程序名
        /// </summary>
        public string AppName { get; set; }
​
        /// <summary>
        /// 电话
        /// </summary>
        private string? phone;
        public string? Phone
        {
            get { return phone; }
            set
            {
                phone = value;
                RaisePropertyChanged("Phone");
            }
        }
​
        /// <summary>
        /// 获取所有用户命令
        /// </summary>
        public DelegateCommand? GetAllUsersCommand { get; set; }
​
        /// <summary>
        /// 构造初始化
        /// </summary>
        public MainWindowViewModel()
        {
            AppName = "WPF MVVM 模式测试";
            Phone = "123456";
            GetAllUsersCommand = new DelegateCommand
            {
                DoExecute = new Action<object?>(GetAllUsersCommandExecute)
            };
        }
​
        /// <summary>
        /// 获取所有用户命令执行方法
        /// </summary>
        private void GetAllUsersCommandExecute(object? paramater)
        {
            Phone = Phone!.Equals("123456") ? "1234567" : "123456";
            UserService userService = new UserService();
            Users = userService.GetAllUser();
        }
    }
}

2.6 设计MainWindow.xaml界面

<Window x:Class="Wpfmvvm_demo.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:Wpfmvvm_demo"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="550">
    <Grid>
        <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
                <Label Content="程序名"></Label>
                <Label Content="{Binding AppName}"></Label>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Content="电话"></Label>
                <Label Content="{Binding Phone}"></Label>
            </StackPanel>
            <StackPanel>
                <Button Content="获取所有用户" Command="{Binding GetAllUsersCommand}"></Button>
            </StackPanel>
            <DataGrid ItemsSource="{Binding Users}" AutoGenerateColumns="False" GridLinesVisibility="All" CanUserDeleteRows="False" CanUserAddRows="False" >
                <DataGrid.Columns>
                    <DataGridTextColumn Header="用户名" Binding="{Binding Name}"></DataGridTextColumn>
                    <DataGridTextColumn Header="密码" Binding="{Binding Password}"></DataGridTextColumn>
                </DataGrid.Columns>
            </DataGrid>
        </StackPanel>
    </Grid>
</Window>

2.7 把ViewModel数据绑定到窗口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Wpfmvvm_demo.ViewModels;
​
namespace Wpfmvvm_demo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }
    }
}

三、MVVM Toolkit

微软虽然提出了 MVVM,但又没有提供一个官方的 MVVM 库(多年前有过 Prism,但已经离家出走了)。每次有人提起 MVVM 库,有些人会推荐 Prism(例如我),有些人会推荐 MVVMLight。可是现在 Prism 已经决定不再支持 UWP , 而 MVVMLight 又不再更新,在这左右为难的时候 Windows Community Toolkit 挺身而出发布了 MVVM Toolkit。 MVVM Toolkit 延续了 MVVMLight 的风格,是一个轻量级的组件,而且它基于 .NET Standard 2.0,可用于UWP, WinForms, WPF, Xamarin, Uno 等多个平台。相比它的前身 MVVMLight,它有以下特点:

  • 更高:版本号更高,一出手就是 7.0。

  • 更快:速度更快,MVVM Toolkit 从一开始就以高性能为实现目标。

  • 更强:后台更强,MVVM Toolkit 的全称是 'Microsoft.Toolkit.Mvvm',根正苗红。

这个包主要提供了如下的

  • Microsoft.Toolkit.Mvvm.ComponentModel

    • ObservableObject

    • ObservableRecipient

    • ObservableValidator

  • Microsoft.Toolkit.Mvvm.DependencyInjection

    • Ioc

  • Microsoft.Toolkit.Mvvm.Input

    • RelayCommand

    • AsyncRelayCommand

    • IRelayCommand

    • IAsyncRelayCommand

  • Microsoft.Toolkit.Mvvm.Messaging

    • IMessenger

    • WeakReferenceMessenger

    • StrongReferenceMessenger

    • IRecipient

    • MessageHandler

  • Microsoft.Toolkit.Mvvm.Messaging.Messages

    • PropertyChangedMessage

    • RequestMessage

    • AsyncRequestMessage

    • CollectionRequestMessage

    • AsyncCollectionRequestMessage

    • ValueChangedMessage

3.1 可观察对象

public class UserVM : ObservableObject
{
  private string name;
​
  public string Name
  {
    get => name;
    set => SetProperty(ref name, value);
  }
​
  private int age;
​
  public int Age
  {
    get => age;
    set => SetProperty(ref age, value);
  }
}

页面的类中添加

public partial class MainWindow : Window
{
  private UserVM userVM = new UserVM();
​
  public MainWindow()
  {
    InitializeComponent();
    DataContext = userVM;
    Task.Run(() =>
             {
               while (true)
               {
                 userVM.Age += 1;
                 Thread.Sleep(1000);
               }
             });
  }
}

页面中

<Grid>
  <StackPanel>
    <TextBlock FontSize="30" Text="{Binding Age}" />
  </StackPanel>
</Grid>

我们就可以看到数字就会一直递增了。

3.2 命令

页面中我们添加命令

<StackPanel Height="40" Orientation="Horizontal">
  <TextBlock FontSize="30" Text="{Binding Name}" />
  <TextBlock FontSize="30" Text="{Binding Age}" />
​
  <Button Command="{Binding IncrementAgeCommand}" Content="年龄递增" />
</StackPanel>

对应的ViewModel中添加命令及响应的事件

public class UserVM : ObservableObject
{
  private string name;
​
  public string Name
  {
    get => name;
    set => SetProperty(ref name, value);
  }
​
  private int age;
​
  public int Age
  {
    get => age;
    set => SetProperty(ref age, value);
  }
​
  public ICommand IncrementAgeCommand { get; }
  
  public UserVM()
  {
    IncrementAgeCommand = new RelayCommand(IncrementAge);
  }
  
  private void IncrementAge() => Age++;
}

这样只要我们点击按钮,年龄就会递增1。

3.3 消息机制

3.3.1 注册与发送

添加传递消息的类

public class ZMessage
{
  public int Code { get; set; }
​
  public string Message { get; set; }
​
  public ZMessage(int code, string msg)
  {
    Code = code;
    Message = msg;
  }
}

消息接收与发送

public MainWindow()
{
  InitializeComponent();
  initMessage();
}
​
private void initMessage()
{
  WeakReferenceMessenger.Default.Register<ZMessage>(
    this,
    (r, m) =>
    {
      Console.WriteLine("接收到信息:" + m.Message);
    }
  );
​
  WeakReferenceMessenger.Default.Send(new ZMessage(100, "Hello"));
}
3.3.2 可接收消息的类

当然我们也可以让我们的ViewModel接收消息

public class UserVM : ObservableRecipient, IRecipient<ZMessage>
{
  private string name;
​
  public string Name
  {
    get => name;
    set => SetProperty(ref name, value);
  }
​
  private int age;
​
  public int Age
  {
    get => age;
    set => SetProperty(ref age, value);
  }
​
  public UserVM()
  {
    IsActive = true;
  }
​
  public void Receive(ZMessage message)
  {
    Name = message.Message;
    Age = message.Code;
  }
}

这里一定要注意

  • 要继承ObservableRecipient类,实现IRecipient<>接口。

  • 只有设置IsActive = true;,才能接收消息。

我们还是在页面的类中发送消息

WeakReferenceMessenger.Default.Send(new ZMessage(18, "XiaoMing"));

页面也稍做修改

<StackPanel Orientation="Horizontal">
  <TextBlock FontSize="30" Text="{Binding Name}" />
  <TextBlock FontSize="30" Text="{Binding Age}" />
</StackPanel>

我们会发现页面上已经变更为我们发送消息的数据了。

3.4 控制反转(IOC)

添加依赖库Microsoft.Extensions.DependencyInjection

Install-Package Microsoft.Extensions.DependencyInjection -Version 6.0.0

创建我们要自动注入的类

加入如下是我们用户相关的服务

public interface IUserService
{
  string getUserName();
}

public class UserService : IUserService
{
  public string getUserName()
  {
    return "XiaoMing";
  }
}

添加注入的控制

public partial class App : Application
{
  public App()
  {
    Services = ConfigureServices();
​
    this.InitializeComponent();
  }
​
  public new static App Current => (App)Application.Current;
​
  public IServiceProvider Services { get; }
​
  private static IServiceProvider ConfigureServices()
  {
    var services = new ServiceCollection();
​
    services.AddSingleton<IUserService, UserService>();
​
    // ViewModels
    services.AddTransient<UserVM>();
​
    return services.BuildServiceProvider();
  }
}

其中

  • AddSingleton 单一实例服务

  • AddScoped 范围内的服务

  • AddTransient 暂时性服务

权重:

AddSingleton`→`AddTransient`→`AddScoped

生命周期:

  • AddSingleton 项目启动-项目关闭 相当于静态类 只会有一个 每一次获取的对象都是同一个

  • AddScoped 请求开始-请求结束 在这次请求中获取的对象都是同一个 请求时创建

  • AddTransient 请求获取-(GC回收-主动释放) 获取时创建 每一次获取的对象都不是同一个

注意:

由于AddScoped对象是在请求的时候创建的 所以不能在AddSingleton对象中使用 甚至也不能在AddTransient对象中使用

使用

private UserVM userVM = (UserVM)App.Current.Services.GetService(typeof(UserVM));
private IUserService userService = (IUserService)App.Current.Services.GetService(typeof(IUserService));

这样是不是感觉还麻烦了

但是如果我们的ViewModel是这样的

public class UserVM : ObservableObject
{
  private string name;
​
  public string Name
  {
    get => name;
    set => SetProperty(ref name, value);
  }
​
  private int age;
​
  public int Age
  {
    get => age;
    set => SetProperty(ref age, value);
  }
​
  public ICommand IncrementAgeCommand { get; }
​
  public IUserService userService { get; }
​
  public UserVM(IUserService _userService)
  {
    userService = _userService;
    IncrementAgeCommand = new RelayCommand(IncrementAge);
  }
​
  private void IncrementAge() => Age++;
}

这里的IUserService的实例并没有传入但是就可以用了,因为IOC框架已经自动注入了。

四、WPF Prism

4.1 常见的MVVM框架

众所周知, 如果你了解WPF当中的ICommand, INotifyPropertyChanged的作用, 就会发现众多框架都是基于这些进行扩展, 实现其通知、绑定、命令等功能。

对于不同的MVVM框架而言, 大体使用上会在声明方式上的差异, 以及特定功能上的差别。

下面列举了常用的3个MVVM框架,他们的一些差异。如下所示:

功能↓ / →框架名PrismMvvmlightMicorosoft.Toolkit.Mvvm
通知BindableBaseViewModelBaseObservableObject
命令DelegateCommandRelayCommandAsync/RelayCommand
聚合器IEventAggregatorIMessengerIMessenger
模块化××
容器××
依赖注入××
导航××
对话××

正如你所见, 各个框架之间都有各自的通知、绑定、事件聚合器等基础的功能, 而Prsim自带的依赖注入、容器、以及导航会话等功能, 可以为你提供更加强大的功能。

当然,在实际的开发过程当中, 可以根据实际的功能需求, 对不同的框架选型, 同时这也需要你对各个框架之间的优缺点进行判断。

那么, 下面将主要介绍Prism

4.2 BindableBase

在Prism当中, 你需要继承于BindableBase

public class TestViewModel : BindableBase
{
    private string _message;
​
    public string Message
    {
        get { return _message; }
        set { _message = value; RaisePropertyChanged(); }
    }
}

在Prism当中, 你可以使用DelegateCommand及带参数的Command

    public class TestViewModel : ViewModelBase
    {
        public DelegateCommand SendCommand { get; set; }
​
        public DelegateCommand<string> SendMessageCommand { get; set; }
    }
4.3 CompositeCommand

对于单个Command而言, 只是触发单个对应的功能, 而复合命令是Prism当中非常强大的功能, CompositeCommand简单来说是一个父命令, 它可以注册N个子命令

当父命令被激活, 它将触发对所有的子命令, 如果任意一个命令CanExecute=false,它将无法被激活,如下所示:

public class MyViewModel : NotificationObject
{
    private readonly CompositeCommand saveAllCommand;
​
    public ArticleViewModel(INewsFeedService newsFeedService,
                            IRegionManager regionManager,
                            IEventAggregator eventAggregator)
    {
        this.saveAllCommand = new CompositeCommand();
        this.saveAllCommand.RegisterCommand(new SaveProductsCommand());
        this.saveAllCommand.RegisterCommand(new SaveOrdersCommand());
    }
​
    public ICommand SaveAllCommand
    {
        get { return this.saveAllCommand; }
    }
}
4.4 IEventAggregator
  • 松耦合基于事件通讯

  • 多个发布者和订阅者

  • 微弱的事件

  • 过滤事件

  • 传递参数

  • 取消订阅

该功能主要作用为, 事件聚合器负责接收订阅以及发布消息。订阅者可以接收到发布者发送的内容。

 
//创建事件
public class SavedEvent : PubSubEvent<string> { }
​
//发布
IEventAggregator.GetEvent<SavedEvent>().Publish("some value");
​
//订阅
IEventAggregator.GetEvent<SavedEvent>().Subscribe(.Subscribe(message=>
                                                             {
                                                                 //do something
                                                             });
4.5 Filtering Events

在实际的开发过程当中,我们往往会在多个位置订阅一个事件, 但是对于订阅者而言, 他并不需要接收任何消息, 如下所示:

在Prism当中, 我们可以指定为事件指定过滤条件, 如下所示:

eventAggregator.GetEvent<MessageSentEvent>()
    .Subscribe(arg =>
               {
                   //do something
               },
               ThreadOption.PublisherThread,
               false,
               //设置条件为token等于“MessageListViewModel” 则接收消息
               message => message.token.Equals(nameof(MessageListViewModel)));

关于Subscribe当中的4个参数, 详解:

  • 1.action: 发布事件时执行的委托。

  • 2.ThreadOption枚举: 指定在哪个线程上接收委托回调。

  • 3.keepSubscriberReferenceAlive: 如果为true,则Prism.Events.PubSubEvent保留对订阅者的引用因此它不会收集垃圾。

  • 4.filter: 进行筛选以评估订阅者是否应接收事件。

4.6 Unsubscribe

为注册的消息取消订阅, Prism提供二种方式取消订阅,如下:

1.通过委托的方式取消订阅

var event = IEventAggregator.GetEvent<MessageSentEvent>();
​
event.Subscribe(OnMessageReceived);
​
event.Unsubscribe(OnMessageReceived);

2.通过获取订阅者token取消订阅

var _event = eventAggregator.GetEvent<MessageSentEvent>();
​
var token = _event.Subscribe(OnMessageReceived);
​
_event.Unsubscribe(token);

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java-03 深入浅出 MyBatis - 快速入门(无 Spring) 增删改查 核心配置讲解 XML 与 注解映射

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

Spring Cloud Alibaba [Gateway]网关。

1 简介 网关作为流量的入口&#xff0c;常用功能包括路由转发、权限校验、限流控制等。而springcloudgateway 作为SpringCloud 官方推出的第二代网关框架&#xff0c;取代了Zuul网关。 1.1 SpringCloudGateway特点: &#xff08;1&#xff09;基于Spring5&#xff0c;支持响应…

Debezium-EmbeddedEngine

提示&#xff1a;一个嵌入式的Kafka Connect源连接器的工作机制 文章目录 前言一、控制流图二、代码分析 1.构造函数2.完成回调3.连接器回调4.RUN总结 前言 工作机制&#xff1a; * 独立运行&#xff1a;嵌入式连接器在应用程序进程中独立运行&#xff0c;不需要Kafka、Kafka C…

阿里斑马智行 2025届秋招 NLP算法工程师

文章目录 个人情况一面/技术面 1h二面/技术面 1h三面/HR面 20min 个人情况 先说一下个人情况&#xff1a; 学校情况&#xff1a;211本中9硕&#xff0c;本硕学校都一般&#xff0c;本硕都是计算机科班&#xff0c;但研究方向并不是NLP&#xff0c;而是图表示学习论文情况&…

社交电商的优势及其与 AI 智能名片小程序、S2B2C 商城系统的融合发展

摘要&#xff1a;本文深入分析了社交电商相较于传统电商的优势&#xff0c;包括门槛低、易操作、更生活化和可团队化运作等特点。同时&#xff0c;探讨了 AI 智能名片小程序和 S2B2C 商城系统在社交电商发展中的作用&#xff0c;以及它们与社交电商融合所带来的新机遇和发展前景…

自动化运维-检测Linux服务器CPU、内存、负载、IO读写、机房带宽和服务器类型等信息脚本

前言&#xff1a;以上脚本为今年8月1号发布的&#xff0c;当时是没有任何问题&#xff0c;但现在脚本里网络速度测试py文件获取不了了&#xff0c;测速这块功能目前无法实现&#xff0c;后面我会抽时间来研究&#xff0c;大家如果有建议也可以分享下。 脚本内容&#xff1a; #…

3D Streaming 在线互动展示系统:NVIDIA RTX 4090 加速实时渲染行业数字化转型

随着科技的飞速发展&#xff0c;实时渲染正逐步成为游戏与实时交互领域的重要驱动力。与离线渲染不同&#xff0c;实时渲染需要极高的计算性能&#xff0c;对硬件设备尤其是GPU的性能要求极高。随着 RTX 4090 显卡的问世&#xff0c;其强大的算力和创新技术&#xff0c;为实时渲…

南京邮电大学《智能控制技术》课后作业

一、问题一 复现二输入单输出模糊控制系统&#xff0c;改动其中一到两个环节&#xff08;隶属度设置、规则等&#xff09;&#xff0c;对比修改前后控制效果。 定义模糊 %Fuzzy Control for water tank clear all; close all;anewfis(fuzz_tank);%Fuzzy Inference System stru…

2.5D视觉——Aruco码定位检测

目录 1.什么是Aruco标记2.Aruco码解码说明2.1 Original ArUco2.2 预设的二维码字典2.3 大小Aruco二维码叠加 3.函数说明3.1 cv::aruco::detectMarkers3.2 cv::solvePnP 4.代码注解4.1 Landmark图说明4.2 算法源码注解 1.什么是Aruco标记 ArUco标记最初由S.Garrido-Jurado等人在…

栈Stack和队列Queue

目录 一、栈 &#xff08;1&#xff09;用数组实现 &#xff08;2&#xff09;用单链表实现 &#xff08;3&#xff09;用标注尾结点的单链表实现 &#xff08;4&#xff09;用双向链表实现 2、栈的实际应用 &#xff08;1&#xff09;改变元素的序列 &#xff08;2&am…

Tailscale 自建 Derp 中转服务器

文章目录 为什么要建立 Derp 中转服务器&#xff1f;安装 Go 环境通过 Go 安装 Derp处理证书文件自签一个域名启动 DerpIPV6 的支持防止 Derp 被白嫖以上的操作命令合集自建 Headscale 添加 Derp参考 为什么要建立 Derp 中转服务器&#xff1f; Tailscale 使用的算法很有趣: 所…

RPC安全可靠的异常重试

当调用方调用服务提供方&#xff0c;由于网络抖动导致的请求失败&#xff0c;这个请求调用方希望执行成功。 调用方应该如何操作&#xff1f;catch异常再发起一次调用&#xff1f;显然不够优雅。这时可以考虑使用RPC框架的重试机制。 RPC框架的重试机制 RPC重试机制&#xff1…

AutoDL部署视觉大模型llama3.2-vision,从视频中寻找特定目标

注&#xff1a; windows11系统。示例为此项目&#xff1a;https://github.com/win4r/VideoFinder-Llama3.2-vision-Ollama 在当今的人工智能领域&#xff0c;深度学习模型的计算需求日益增长&#xff0c;特别是在处理复杂的视觉任务时&#xff0c;强大的算力往往是实现高效应用…

【大语言模型】ACL2024论文-16 基于地图制图的罗马尼亚自然语言推理语料库的新型课程学习方法

【大语言模型】ACL2024论文-16 基于地图制图的罗马尼亚自然语言推理语料库的新型课程学习方法 目录 文章目录 【大语言模型】ACL2024论文-16 基于地图制图的罗马尼亚自然语言推理语料库的新型课程学习方法目录摘要&#xff1a;研究背景&#xff1a;问题与挑战&#xff1a;如何解…

golang调用模组程序实现交互输入自动化,获取imei及iccid

应用场景&#xff1a;在openwrt下调用移远的测试程序&#xff0c;并实现输入自动话&#xff0c;获取imei rootOpenWrt:~# ql-api-test Test groups:0: ql_dsi1: ql_nw2: ql_sim3: ql_dev4: ql_voice5: ql_sms6: ql_adc7: ql_i2c8: …

【数据分享】2022年我国10米分辨率茶树种植分布栅格数据

小麦、玉米、水稻、茶树等各类农作物的种植分布数据在农业、环境、国土等很多专业都经常用到&#xff01; 本次给大家分享的是我国2022年10米分辨率茶树种植分布栅格数据&#xff01;数据格式为TIFF格式。数据坐标为GCS_WGS_1984。数据格式为TIFF格式。数据坐标为GCS_WGS_1984…

【弱监督视频异常检测】2024-ESWA-基于扩散的弱监督视频异常检测常态预训练

2024-ESWA-Diffusion-based normality pre-training for weakly supervised video anomaly detection 基于扩散的弱监督视频异常检测常态预训练摘要1. 引言2. 相关工作3. 方法论3.1. 使用扩散自动编码器进行常态学习3.2. 全局-局部特征编码器3.2.1 局部块3.2.2 全局块3.2.3 协同…

vue实现展示并下载后端返回的图片流

// 点击下载 downLoadCode() {const image new Image();image.setAttribute("crossOrigin", "anonymous");image.onload () > {const canvas document.createElement("canvas");canvas.width image.width;canvas.height image.height;c…

STL关联式容器之平衡二叉搜索树

平衡二叉搜索树 在STL关联式容器介绍-CSDN博客中对二叉搜索树做了简要的描述&#xff1b;但是因为没有对二叉搜索树对数的深度及插入后树的结构进行调整&#xff0c;二叉搜索树可能失去平衡&#xff0c;造成搜寻效率低落的情况。如下所示&#xff1a; 所谓树形平衡与否&#xf…

Django启用国际化支持(2)—实现界面内切换语言:activate()

文章目录 ⭐注意⭐1. 配置项目全局设置&#xff1a;启用国际化2. 编写视图函数3. 配置路由4. 界面演示5、扩展自动识别并切换到当前语言设置语言并保存到Session设置语言并保存到 Cookie ⭐注意⭐ 以下操作依赖于 Django 项目的国际化支持。如果你不清楚如何启用国际化功能&am…