WPF框架---MvvmLight介绍

目录

1. MvvmLight 框架准备

2. MvvmLight 中的相关基类

3. MvvmLight 中的数据绑定与通知

a. 核心功能

b. 关键方法与属性

c. 完整示例

d. 高级用法

4. MvvmLight 中的命令对象

a. 命令对象的作用

b. 核心接口:ICommand

c. MvvmLight 中的 RelayCommand

d. 动态更新命令的可执行状态

e. 高级用法

5. Messenger 对象使用

a. Messenger 的核心作用

b. MvvmLight 中的 Messenger 实现

c. 基本使用场景

d. 高级用法

6. DispatcherHelper 对象使用

7. SimpleIoc 对象使用

a. SimpleIoc 的核心作用

b. 核心方法与属性

c. 基本使用步骤

d. 高级用法

e. 与 ViewModelLocator 结合使用


1. MvvmLight 框架准备

  • 作用: 快速搭建 MVVM 架构的应用程序,简化数据绑定、命令和消息传递。
  • 步骤: 通过 NuGet 安装 MvvmLightLibs 包。

2. MvvmLight 中的相关基类

  • 核心基类:
    • ViewModelBase: ViewModel 基类,实现 INotifyPropertyChanged
public class MainViewModel : ViewModelBase
{private string _name;public string Name{get => _name;set => Set(ref _name, value); // 自动触发 PropertyChanged}
}
    • ObservableObject: 轻量级可观察对象。
    • RelayCommand: 命令对象基类。

3. MvvmLight 中的数据绑定与通知

a. 核心功能

ViewModelBase 继承自 ObservableObject,并实现了 INotifyPropertyChanged 接口,主要负责:

  • 属性变更通知:当 ViewModel 的某个属性值发生变化时,自动通知 UI 更新。
  • 简化代码:通过 Set 方法简化属性定义,避免手动触发 PropertyChanged 事件。
  • 设计模式支持:提供静态属性 IsInDesignMode,用于区分代码是在设计时(如 Visual Studio 设计器)还是运行时执行。
b. 关键方法与属性

(1) Set<T>(ref T field, T value, [CallerMemberName] string propertyName = null)

  • 作用:在属性的 set 方法中调用此方法,自动比较新旧值,若不同则更新字段并触发 PropertyChanged 事件。
  • 示例
private string _name;
public string Name
{get => _name;set => Set(ref _name, value); // 自动触发通知
}

(2)RaisePropertyChanged(string propertyName)

  • 作用:手动触发某个属性的 PropertyChanged 事件。
  • 场景:当某个属性的值依赖于其他属性时,手动通知 UI 更新。
public string FullName => $"{FirstName} {LastName}";private string _firstName;
public string FirstName
{get => _firstName;set{Set(ref _firstName, value);RaisePropertyChanged(nameof(FullName)); // 通知 FullName 属性变化}
}

(3) IsInDesignMode

  • 作用:静态属性,判断当前代码是否在设计器(如 Visual Studio 或 Blend)中运行。
  • 用途:在设计时提供假数据,避免调用真实服务或数据库。
public class MainViewModel : ViewModelBase
{public MainViewModel(){if (IsInDesignMode)Name = "Design Mode Sample"; // 设计器显示假数据elseLoadRealData(); // 运行时加载真实数据}
}
c. 完整示例
using GalaSoft.MvvmLight;public class UserViewModel : ViewModelBase
{private string _userName;private int _age;public string UserName{get => _userName;set => Set(ref _userName, value);}public int Age{get => _age;set => Set(ref _age, value);}// 计算属性(依赖其他属性)public string UserInfo => $"{UserName} (Age: {Age})";// 当 UserName 或 Age 变化时,手动通知 UserInfo 更新protected override void OnPropertyChanged(string propertyName = null){base.OnPropertyChanged(propertyName);if (propertyName == nameof(UserName) || propertyName == nameof(Age))RaisePropertyChanged(nameof(UserInfo));}
}
d. 高级用法

(1)批量通知多个属性

通过 RaisePropertyChanged(null) 或指定空字符串,通知所有属性更新(慎用,可能影响性能):

public void ResetAllProperties()
{_userName = "Default";_age = 0;RaisePropertyChanged(""); // 通知所有属性更新
}

(2) 继承与扩展

可继承 ViewModelBase 并添加通用逻辑(如日志记录、验证):

public abstract class CustomViewModelBase : ViewModelBase
{protected void LogPropertyChange(string propertyName){Debug.WriteLine($"属性 {propertyName} 已更新");}public override void RaisePropertyChanged(string propertyName = null){base.RaisePropertyChanged(propertyName);LogPropertyChange(propertyName);}
}


4. MvvmLight 中的命令对象

a. 命令对象的作用
  • 解耦 UI 与业务逻辑:将用户操作(如点击按钮)映射到 ViewModel 的方法。
  • 控制可执行状态:根据条件动态启用或禁用 UI 元素(例如按钮的 IsEnabled)。
  • 支持参数传递:允许从 UI 传递参数到 ViewModel(如选中项的 ID)。
b. 核心接口:ICommand

所有命令对象均实现 System.Windows.Input.ICommand 接口,其定义如下:

public interface ICommand
{event EventHandler CanExecuteChanged; // 通知命令可执行状态变化bool CanExecute(object parameter);    // 判断命令是否可执行void Execute(object parameter);       // 执行命令逻辑
}
c. MvvmLight 中的 RelayCommand

MvvmLight 提供 RelayCommandRelayCommand<T> 类,简化了 ICommand 的实现。

(1) 基本用法(无参数)

public class MainViewModel : ViewModelBase
{public RelayCommand SaveCommand { get; }public MainViewModel(){// 初始化命令:绑定方法 + 可执行条件SaveCommand = new RelayCommand(SaveData, CanSave);}private void SaveData(){// 保存逻辑}private bool CanSave(){return !string.IsNullOrEmpty(Name); // 仅当 Name 非空时按钮可用}
}

(2) 支持参数传递(RelayCommand<T>)

public RelayCommand<string> FilterCommand { get; }public MainViewModel()
{FilterCommand = new RelayCommand<string>(param => ApplyFilter(param), param => !string.IsNullOrEmpty(param));
}private void ApplyFilter(string keyword)
{// 根据关键字过滤数据
}
d. 动态更新命令的可执行状态

(1) 手动触发更新

CanExecute 依赖的属性变化时,调用 RelayCommandRaiseCanExecuteChanged 方法:

private string _name;
public string Name
{get => _name;set{Set(ref _name, value);SaveCommand.RaiseCanExecuteChanged(); // 触发重新检查 CanSave}
}

(1) 自动触发更新

利用 ViewModelBaseSet 方法自动触发属性变更通知,无需手动调用 RaiseCanExecuteChanged

private string _name;
public string Name
{get => _name;set => Set(ref _name, value); // Set 方法已自动触发 PropertyChanged
}// CanSave 方法中依赖 Name 属性
private bool CanSave() => !string.IsNullOrEmpty(Name);
e. 高级用法

(1) 异步命令

直接在命令中执行异步操作时,需注意线程安全(通过 DispatcherHelper):

public RelayCommand LoadDataCommand { get; }public MainViewModel()
{LoadDataCommand = new RelayCommand(async () => await LoadDataAsync());
}private async Task LoadDataAsync()
{try{IsLoading = true;var data = await _dataService.GetData();// 更新 UI(确保在 UI 线程)DispatcherHelper.CheckBeginInvokeOnUI(() => DataList = data);}finally{IsLoading = false;}
}

(2) 复合命令

将多个命令组合成一个逻辑操作:

public RelayCommand SubmitAllCommand { get; }public MainViewModel()
{SubmitAllCommand = new RelayCommand(() =>{SaveCommand.Execute(null);LogCommand.Execute(null);}, () => SaveCommand.CanExecute(null) && LogCommand.CanExecute(null));
}

(3) 命令的泛型约束

public RelayCommand<int> DeleteItemCommand { get; }public MainViewModel()
{DeleteItemCommand = new RelayCommand<int>(id => DeleteItem(id), id => id > 0);
}private void DeleteItem(int itemId)
{// 删除指定 ID 的项
}

5. Messenger 对象使用

MVVM 模式 中,Messenger 是一个用于实现 松耦合通信 的核心组件,尤其在 MvvmLight 框架 中被广泛使用。它允许不同组件(如 ViewModel、View、服务)之间通过消息进行通信,而无需直接引用彼此,从而降低依赖、提升代码可维护性。

a. Messenger 的核心作用
  • 解耦组件通信:组件无需持有对方引用,通过消息订阅/发布机制交互。
  • 跨层级传递数据:例如从子 ViewModel 通知父 ViewModel,或跨页面传递状态。
  • 支持复杂场景:如广播通知、请求-响应模式、事件聚合等。
b. MvvmLight 中的 Messenger 实现

MvvmLight 的 Messenger 类是一个静态单例(Messenger.Default),提供消息的注册、发送和注销功能。其核心方法如下:

方法

作用

Register<TMessage>(object recipient, Action<TMessage> action)

订阅类型为 TMessage

的消息。recipient

为接收者标识(通常为 this

)。

Send<TMessage>(TMessage message)

发送一条 TMessage

类型的消息,所有订阅者会收到通知。

Unregister(object recipient)

注销某个接收者的所有消息订阅,避免内存泄漏。

c. 基本使用场景

(1) 发送简单通知消息

  • 场景:ViewModel A 完成数据加载后,通知 ViewModel B 刷新界面。

发送消息方

// 发送一个无参数通知
Messenger.Default.Send(new NotificationMessage("DataLoaded"));

接收消息方(ViewModel B)

public ViewModelB()
{// 注册接收 NotificationMessage 类型的消息Messenger.Default.Register<NotificationMessage>(this, message =>{if (message.Notification == "DataLoaded"){RefreshData();}});
}

(2) 传递数据对象

  • 场景:用户选择某条数据后,跨页面传递选中项。

定义消息类型

public class ItemSelectedMessage
{public int ItemId { get; set; }public ItemSelectedMessage(int id) => ItemId = id;
}

发送方:

// 用户选择某项后发送消息
Messenger.Default.Send(new ItemSelectedMessage(selectedItem.Id));

接收方:

Messenger.Default.Register<ItemSelectedMessage>(this, message =>
{LoadItemDetails(message.ItemId); // 根据 ItemId 加载详情
});
d. 高级用法

(1) 消息令牌(Token)

  • 作用:区分相同消息类型的不同用途,避免消息冲突。

发送带令牌的消息

// 发送消息时指定令牌
Messenger.Default.Send(new NotificationMessage("UpdateChart"), "ChartToken" // 令牌标识
);

接收指定令牌的消息

Messenger.Default.Register<NotificationMessage>(this, message => UpdateChartData(), "ChartToken" // 仅接收带有此令牌的消息
);

(2) 泛型消息

  • 场景:传递强类型数据,避免类型转换。

定义泛型消息

public class GenericMessage<T>
{public T Content { get; }public GenericMessage(T content) => Content = content;
}

发送泛型消息

var data = new List<User>();
Messenger.Default.Send(new GenericMessage<List<User>>(data));

接收泛型消息

Messenger.Default.Register<GenericMessage<List<User>>>(this, message =>
{UserList = message.Content; // 直接获取 List<User>
});

(3) 双向通信(请求-响应模式)

  • 场景:ViewModel A 请求数据,ViewModel B 响应返回结果。

发送请求消息

public class DataRequestMessage
{public Action<string> Callback { get; set; } // 定义回调委托
}// 发送请求,并注册回调
Messenger.Default.Send(new DataRequestMessage 
{ Callback = response => HandleResponse(response) 
});

接收请求并响应

Messenger.Default.Register<DataRequestMessage>(this, message =>
{var data = FetchDataFromService(); // 获取数据message.Callback?.Invoke(data);    // 执行回调
});

6. DispatcherHelper 对象使用

  • 作用: 在非 UI 线程更新 UI。
  • 初始化:
DispatcherHelper.Initialize(); // 在 App.xaml.cs 中调用
  • 使用:
Task.Run(() =>{// 后台线程操作DispatcherHelper.CheckBeginInvokeOnUI(() =>{// 更新 UI 元素StatusText = "Processing...";});});

7. SimpleIoc 对象使用

SimpleIocMvvmLight 框架中提供的一个轻量级依赖注入(Dependency Injection, DI)容器,用于管理应用程序中各个组件(如 ViewModel、服务、数据源)的依赖关系。它通过 控制反转(IoC)依赖注入 机制,帮助开发者实现代码解耦、提高可测试性和可维护性。

a. SimpleIoc 的核心作用
  • 解耦组件依赖:将类的依赖关系从代码中抽离,通过容器统一管理。
  • 单例生命周期管理:默认以单例模式提供实例,避免重复创建对象。
  • 简化实例获取:通过接口或类型直接获取已注册的实例。
  • 支持构造函数注入:自动解析构造函数参数,完成依赖注入。
b. 核心方法与属性

以下是 SimpleIoc 的常用方法及其功能:

方法/属性

作用

Register<TInterface, TClass>()

注册接口 TInterface

和其实现类 TClass

Register<TClass>()

直接注册类型 TClass

(无接口)。

GetInstance<T>()

获取类型 T

的实例(已注册的接口或类)。

ContainsCreated<T>()

检查类型 T

的实例是否已被创建。

Reset()

重置容器,清空所有注册和实例(用于测试或重新初始化)。

IsRegistered<T>()

检查类型 T

是否已注册。

c. 基本使用步骤

(1) 注册服务与 ViewModel

在应用程序启动时(如 ViewModelLocatorApp.xaml.cs 中),注册所有依赖项:

public class ViewModelLocator
{public ViewModelLocator(){// 注册服务(接口 + 实现类)SimpleIoc.Default.Register<IDataService, DataService>();// 注册 ViewModel(无接口)SimpleIoc.Default.Register<MainViewModel>();}// 提供 ViewModel 实例的公共属性public MainViewModel Main => SimpleIoc.Default.GetInstance<MainViewModel>();
}

(2) 获取实例

通过 GetInstance<T> 方法获取已注册的实例:

// 获取 ViewModel 实例
var mainVM = SimpleIoc.Default.GetInstance<MainViewModel>();// 获取服务实例
var dataService = SimpleIoc.Default.GetInstance<IDataService>();

(3) 构造函数注入

当注册的类(如 ViewModel)依赖其他服务时,SimpleIoc 会自动解析构造函数参数:

public class MainViewModel : ViewModelBase
{private readonly IDataService _dataService;// 构造函数依赖注入:自动传入已注册的 IDataService 实例public MainViewModel(IDataService dataService){_dataService = dataService;}
}
d. 高级用法

(1) 单例模式 vs. 瞬时模式

单例模式(默认):整个应用程序生命周期内只创建一个实例。

// 默认单例模式
SimpleIoc.Default.Register<IDataService, DataService>();

瞬时模式:每次调用 GetInstance 时创建新实例。

SimpleIoc.Default.Register<IDataService, DataService>(createInstanceImmediately: false);
var service = SimpleIoc.Default.GetInstance<IDataService>(); // 每次返回新实例

(2) 手动指定实例

允许直接注册一个已存在的对象实例:

var logger = new FileLogger();
SimpleIoc.Default.Register<ILogger>(() => logger); // 注册现有实例

(3) 依赖覆盖

在测试中,可以替换实现类以注入 Mock 对象:

// 生产环境注册真实服务
SimpleIoc.Default.Register<IDataService, DataService>();// 测试环境覆盖为 Mock 服务
SimpleIoc.Default.Unregister<IDataService>();
SimpleIoc.Default.Register<IDataService, MockDataService>();
e. 与 ViewModelLocator 结合使用

ViewModelLocator 是 MvvmLight 中用于集中管理 ViewModel 的类,通常与 SimpleIoc 配合使用,通过 XAML 绑定 ViewModel。

(1) 定义 ViewModelLocator

public class ViewModelLocator
{public ViewModelLocator(){// 注册服务和 ViewModelSimpleIoc.Default.Register<IDataService, DataService>();SimpleIoc.Default.Register<MainViewModel>();}// 暴露 ViewModel 属性供 XAML 绑定public MainViewModel Main => SimpleIoc.Default.GetInstance<MainViewModel>();
}

(2) 在 XAML 中声明资源

App.xaml 中合并 ViewModelLocator

<Application.Resources><ResourceDictionary><vm:ViewModelLocator x:Key="Locator" /></ResourceDictionary>
</Application.Resources>

(3) 在 View 中绑定 ViewModel

<Window DataContext="{Binding Main, Source={StaticResource Locator}}"><!-- UI 元素绑定到 MainViewModel 的属性 -->
</Window>


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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

公司网络安全组织结构

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 第17章 网络安全应急响应技术原理与应用 17.1 网络安全应急响应概述 居安思危&#xff0c;思则有备&#xff0c;有备无患。网络安全应急响应是针对潜在发生的网络…

《深度学习进阶》第7集:深度实战 通过训练一个智能体玩游戏 来洞察 强化学习(RL)与决策系统

深度学习进阶 | 第7集&#xff1a;深度实战 通过训练一个智能体玩游戏 来洞察 强化学习&#xff08;RL&#xff09;与决策系统 在深度学习的广阔领域中&#xff0c;强化学习&#xff08;Reinforcement Learning, RL&#xff09;是一种独特的范式&#xff0c;它通过智能体与环境…

【web前端开发】CSS--CSS简介及其编写位置(上)

1、CSS简介 &#xff08;1&#xff09;CSS的全称为&#xff1a;层叠式样式表&#xff08;Cascading Style Sheets&#xff09; &#xff08;2&#xff09;CSS也是一种标记语言&#xff0c;用于给HTML结构设置样式&#xff0c;例如&#xff1a;文字大小、颜色、元素宽度等等…

计算机视觉|Swin Transformer:视觉 Transformer 的新方向

一、引言 在计算机视觉领域的发展历程中&#xff0c;卷积神经网络&#xff08;CNN&#xff09; 长期占据主导地位。从早期的 LeNet 到后来的 AlexNet、VGGNet、ResNet 等&#xff0c;CNN 在图像分类、目标检测、语义分割等任务中取得了显著成果。然而&#xff0c;CNN 在捕捉全…

UE4 组件 (对话组件)

制作一个可以生成对话气泡&#xff0c;显示对话台词的简单组件。这个组件要的变量&#xff1a;台词&#xff08;外部传入&#xff09;。功能&#xff1a;开始对话&#xff08;生成气泡UI&#xff09; &#xff0c;结束对话。 一、对话组件创建 二、开始对话事件 1、注意这里获…

WPF高级 | WPF 应用程序部署与发布:确保顺利交付到用户手中

WPF高级 | WPF 应用程序部署与发布&#xff1a;确保顺利交付到用户手中 一、前言二、部署与发布基础概念2.1 部署的定义与目的2.2 发布的方式与渠道2.3 部署与发布的关键要素 三、WPF 应用程序打包3.1 使用 Visual Studio 自带的打包工具3.2 使用第三方打包工具 四、发布到不同…

Vue3自定义hooks

Vue3自定义hooks 什么是自定义hooks 把一个功能的所有数据和方法写到同一个ts文件里。 命名规范&#xff1a;useXxx&#xff0c;小驼峰。 在每一个hooks中&#xff0c;都可以写onMounted、computed等属性&#xff0c;互不干扰。 体现出了组合式AI的优势&#xff0c;一个功能的…

Win7重装不翻车!ISO镜像安全下载渠道+BIOS设置避雷手册

一、写在前面&#xff1a;为什么你需要这份教程&#xff1f; 当电脑频繁蓝屏、系统崩溃甚至无法开机时&#xff0c;重装系统可能是最后的救命稻草。但市面上的教程往往存在三大痛点&#xff1a; ⚠️ 镜像来源不明导致系统被植入后门 ⚠️ 启动盘制作失败反复折腾 ⚠️ 操作失…

DeepSeek大模型 —— 全维度技术解析

DeepSeek大模型 —— 全维度技术解析 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff01;点我试试&#xff01;&#xff01; 文章目录 DeepSeek大模型 —— 全维度技术解析一、模型架构全景解析1.1…

AGI 之 【Dify】 之 使用 Docker 在 Windows 端本地部署 Dify 大语言模型(LLM)应用开发平台

AGI 之 【Dify】 之 使用 Docker 在 Windows 端本地部署 Dify 大语言模型&#xff08;LLM&#xff09;应用开发平台 目录 AGI 之 【Dify】 之 使用 Docker 在 Windows 端本地部署 Dify 大语言模型&#xff08;LLM&#xff09;应用开发平台 一、简单介绍 二、Docker 下载安…

外键 FK enable constraints enq:TM lock

ALTER TABLE X ENABLE NOVALIDATE CONSTRAINT XX&#xff1b; 如果referenced 表在DML 就会出现 enq:TM lock

深度学习语义分割数据集全景解析

一、语义分割任务概述 语义分割是计算机视觉领域的核心任务之一&#xff0c;目标是通过算法将图像中的每个像素精准划分到对应的语义类别&#xff08;如道路、车辆、行人等&#xff09;。高质量标注数据集是推动该领域发展的关键因素。本文将系统梳理主流数据集的技术特征与适…

【芯片设计】处理器芯片大厂前端设计工程师面试记录·20250307

面试问题和ds的参考答案 好的,我现在需要处理用户的请求,他们希望我扩展之前的回答,使其更详细、更丰富。首先,我需要回顾之前的回答,看看哪些部分可以进一步展开。用户提到“回答多说一些!”,这意味着他们希望每个问题的回答内容更深入,包含更多的技术细节、实际案例…

C++特殊类设计

目录 不能被拷贝的类 只能在堆上创建对象的类 构造函数私有化&#xff1a; 析构函数私有化&#xff1a; 只能在栈上创建对象的类 不能被继承的类 只能创建一个对象的类(单例模式) 设计模式&#xff1a; 单例模式&#xff1a; 饿汉模式&#xff1a; 懒汉模式&#xff1a; 线程…

Linux系统之配置HAProxy负载均衡服务器

Linux系统之配置HAProxy负载均衡服务器 前言一、HAProxy介绍1.1 HAProxy简介1.2 主要特点1.3 使用场景二、本次实践介绍2.1 本次实践简介2.2 本次实践环境规划三、部署两台web服务器3.1 运行两个Docker容器3.2 编辑测试文件3.3 访问测试四、安装HAProxy4.1 更新系统软件源4.2 安…

从零开始学机器学习——什么是机器学习

这个系列的文章旨在为初学者提供机器学习知识&#xff0c;避免使用专业术语和复杂的概念&#xff0c;以便更好地理解和应用。 首先给大家介绍一个很好用的学习地址&#xff1a;https://cloudstudio.net/columns 机器学习 在这里简要介绍机器学习&#xff1a;它利用真实世界或…

AI驱动的消费者体验优化——DeepBI如何用智能策略助力亚马逊卖家“俘获”消费者的心

在亚马逊这个竞争异常激烈的电商平台上&#xff0c;消费者体验已经成为决定卖家成败的关键因素之一。优质的消费者体验不仅能够提升客户满意度&#xff0c;还能加速口碑传播&#xff0c;带动销量持续增长。 今天&#xff0c;我们就来深入解析&#xff0c;DeepBI如何依托 AI 驱动…

Devart dbForge Studio for MySQL Enterprise 9.0.338高效数据库管理工具

Devart dbForge Studio for MySQL Enterprise 9.0.338 是一款功能强大的 MySQL 数据库管理工具&#xff0c;专为数据库开发人员和管理员设计。它提供了丰富的功能&#xff0c;帮助用户更高效地管理、开发和维护 MySQL 数据库 Devart dbForge Studio for MySQL Enterprise 9.0.…

SQL_语法

1 数据库 1.1 新增 create database [if not exists] 数据库名; 1.2 删除 drop database [if exists] 数据库名; 1.3 查询 (1) 查看所有数据库 show databases; (2) 查看当前数据库下的所有表 show tables; 2 数据表 2.1 新增 (1) 创建表 create table [if not exists…

PX4中的UAVCAN_V1的实现库libcanard与数据格式DSDL

libcanard简介 libcanard 是用于嵌入式实时系统的 UAVCAN 协议的缩减版实现库。 在 PX4 中&#xff0c;在 src/drivers/uacvan_v1 目录中&#xff0c;与 0.9 版本的相比&#xff0c;名称更改了(v0.9版本为 libuavcan)&#xff1a; 此库有几个点需要注意&#xff1a; 1&#…