C# MVVM实战:从零开始构建一个WPF登录应用(含完整代码)

news2026/3/14 3:19:34
C# MVVM实战从零开始构建一个WPF登录应用含完整代码如果你是一位C#开发者正在从WinForms或简单的WPF事件驱动模式转向更结构化的开发方式那么MVVM架构很可能已经出现在你的雷达上。它听起来很美好——清晰的职责分离、易于测试、UI与逻辑解耦——但当你真正打开Visual Studio面对一个空白的WPF项目时那种“从何下手”的茫然感可能会瞬间袭来。理论文章读了不少可一到实战绑定不生效、命令没反应、数据不更新一堆问题接踵而至。这篇文章就是为你准备的。我们不谈空洞的概念直接动手用大约三百行代码构建一个功能完整、结构清晰的WPF登录应用。你会看到INotifyPropertyChanged如何让界面“活”起来ICommand如何优雅地处理按钮点击以及ViewModel如何成为连接数据和界面的桥梁。整个过程就像搭积木我们会从最基础的Model开始一步步组装直到看到一个可以交互、有基础验证的登录窗口。准备好了吗让我们打开Visual Studio开始这次实战之旅。1. 项目初始化与核心Model构建在开始敲代码之前我们先明确这个登录应用需要什么。最核心的是一个代表“用户”的数据模型。在MVVM的世界里Model是业务的基石它不关心界面长什么样只关心数据本身和与数据相关的核心规则。1.1 创建项目与解决方案结构首先启动Visual Studio2022或2019均可创建一个新的WPF应用项目命名为WpfLoginDemo。为了保持代码的整洁和可维护性我强烈建议在项目中建立清晰的文件夹结构。这不仅仅是美观更是为后续可能的功能扩展打下基础。在“解决方案资源管理器”中右键点击项目依次创建以下几个文件夹Models: 用于存放所有数据模型类。ViewModels: 用于存放所有视图模型类。Views: 用于存放所有窗口和用户控件XAML文件。Common或Utilities: 用于存放一些通用的辅助类比如我们后面会用到的命令中继器。提示这种文件夹结构是中型WPF项目的常见实践。对于非常小的demo你可以不这么做但养成习惯对未来大有裨益。1.2 实现User模型类现在让我们在Models文件夹下创建第一个类。右键点击Models文件夹选择“添加” - “类”命名为User.cs。这个User类非常简单它就是一个纯数据对象POCO。它的职责是定义用户有哪些属性以及封装一些最基础、不依赖于任何UI框架的业务规则。注意它没有任何对WPF命名空间的引用。namespace WpfLoginDemo.Models { public class User { public string Username { get; set; } public string Password { get; set; } // 一个简单的模型层验证示例 public bool ValidateCredentials(string inputUsername, string inputPassword) { // 在实际项目中这里可能是查询数据库或调用API // 此处仅为演示进行简单的非空和相等判断 return !string.IsNullOrEmpty(inputUsername) !string.IsNullOrEmpty(inputPassword) inputUsername Username inputPassword Password; } } }这个模型类有几个关键点属性简单明了只有用户名和密码。在实际场景中你可能会加入邮箱、ID、创建时间等更多属性。包含业务逻辑ValidateCredentials方法封装了“验证凭据”这个业务规则。虽然这里逻辑很简单但它体现了Model的职责——处理数据逻辑。未来这个方法内部可以轻松替换为调用Web API或查询数据库而不会影响上层的ViewModel和View。独立于UI整个类没有引用System.Windows或任何XAML相关的东西这意味着你可以把它用在控制台应用、单元测试或者其他任何类型的.NET项目中。Model构建完成后我们的数据基石就准备好了。接下来我们需要一个“翻译官”和“调度员”将Model的数据转换成View能显示的样子并处理View发来的用户操作指令这就是ViewModel的舞台。2. ViewModel数据与命令的中枢如果说Model是后台的仓库管理员那么ViewModel就是前台的客户经理。它从Model那里取货数据进行适当的包装和整理格式转换、状态管理然后展示给客户View。同时它也接收客户的订单用户操作并转交给仓库去处理。这个过程中最关键的两个机制是属性变更通知和命令绑定。2.1 实现属性变更通知 (INotifyPropertyChanged)WPF的数据绑定之所以强大是因为它支持双向通信。当界面上的文本框内容改变时后台的数据要能自动更新反之当后台数据改变时界面也要能自动刷新。这一切的魔法都源于INotifyPropertyChanged接口。我们先在ViewModels文件夹下创建LoginViewModel.cs。为了让其属性支持通知我们需要实现这个接口。using System.ComponentModel; using System.Runtime.CompilerServices; using WpfLoginDemo.Models; namespace WpfLoginDemo.ViewModels { public class LoginViewModel : INotifyPropertyChanged { private string _username; private string _password; private string _statusMessage; public event PropertyChangedEventHandler? PropertyChanged; // 这是触发属性变更通知的核心方法 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public string Username { get _username; set { if (_username ! value) { _username value; OnPropertyChanged(); // 通知UIUsername属性已变更 } } } public string Password { get _password; set { if (_password ! value) { _password value; OnPropertyChanged(); // 通知UIPassword属性已变更 } } } public string StatusMessage { get _statusMessage; set { if (_statusMessage ! value) { _statusMessage value; OnPropertyChanged(); } } } } }代码解析INotifyPropertyChanged接口要求我们实现一个PropertyChanged事件。OnPropertyChanged方法是一个辅助方法用于安全地触发这个事件。[CallerMemberName]这个特性是C#的语法糖它能让编译器自动将调用此方法处的属性名如Username作为参数传入这样我们写OnPropertyChanged()就行了无需手动写OnPropertyChanged(nameof(Username))既简洁又不易出错。在每个属性的setter中我们判断新值是否与旧值不同。如果不同则更新字段并调用OnPropertyChanged()。这个判断很重要避免了不必要的UI刷新。2.2 实现命令与中继器 (ICommand)在MVVM中我们不用传统的Click事件处理程序而是使用命令Command。命令将用户操作如点击按钮抽象成一个可绑定的对象它自带一个“是否可以执行”的状态。WPF的按钮绑定命令后会自动根据这个状态来启用或禁用自身。.NET提供了ICommand接口但我们需要一个具体的实现。通常我们会创建一个通用的RelayCommand或DelegateCommand。在Common文件夹下创建RelayCommand.cs。using System; using System.Windows.Input; namespace WpfLoginDemo.Common { public class RelayCommand : ICommand { private readonly Actionobject _execute; private readonly Predicateobject _canExecute; public RelayCommand(Actionobject execute, Predicateobject canExecute null) { _execute execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute canExecute; } public bool CanExecute(object parameter) { return _canExecute null || _canExecute(parameter); } public void Execute(object parameter) { _execute(parameter); } // 这个事件是ICommand接口要求的用于在命令的可执行状态可能改变时通知UI public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested value; } remove { CommandManager.RequerySuggested - value; } } } }这个RelayCommand类封装了执行逻辑(_execute)和判断逻辑(_canExecute)。CommandManager.RequerySuggested是一个WPF内置的机制它会在UI操作如输入文本、焦点变化时自动触发询问所有命令的CanExecute状态是否改变从而自动更新按钮的启用状态。现在我们回到LoginViewModel添加登录命令和相关的业务逻辑。using System.Windows.Input; using WpfLoginDemo.Common; using WpfLoginDemo.Models; namespace WpfLoginDemo.ViewModels { public class LoginViewModel : INotifyPropertyChanged { private User _user; // ... 之前的属性Username, Password, StatusMessage和事件 ... public ICommand LoginCommand { get; } public ICommand ResetCommand { get; } public LoginViewModel() { // 初始化一个模拟用户实际项目中应从服务加载 _user new User { Username admin, Password 123456 }; StatusMessage 请输入用户名和密码。; // 初始化命令 LoginCommand new RelayCommand(ExecuteLogin, CanExecuteLogin); ResetCommand new RelayCommand(ExecuteReset); } private bool CanExecuteLogin(object parameter) { // 只有当用户名和密码都不为空时登录按钮才可用 return !string.IsNullOrWhiteSpace(Username) !string.IsNullOrWhiteSpace(Password); } private void ExecuteLogin(object parameter) { // 调用Model的业务逻辑进行验证 bool isValid _user.ValidateCredentials(Username, Password); if (isValid) { StatusMessage $登录成功欢迎您{Username}。; // 在实际应用中这里通常会导航到主窗口或关闭登录窗口 } else { StatusMessage 登录失败用户名或密码错误。; } } private void ExecuteReset(object parameter) { Username string.Empty; Password string.Empty; StatusMessage 信息已重置。; } } }至此ViewModel的核心部分已经完成。它拥有数据属性(Username,Password,StatusMessage): 与View双向绑定。命令(LoginCommand,ResetCommand): 响应View的用户交互。业务协调在ExecuteLogin中它协调了Model(_user.ValidateCredentials) 和自身状态 (StatusMessage) 的更新。ViewModel已经准备就绪它正等待着与一个漂亮的界面连接起来。3. View使用XAML构建用户界面View是用户看到并与之交互的部分。在WPF中View主要由XAML文件定义。我们的目标是创建一个清晰、可绑定的登录界面将UI控件与ViewModel中的属性和命令连接起来。这里的关键在于理解并正确使用数据绑定语法。3.1 设计登录窗口布局首先我们需要设置窗口的DataContext。DataContext是整个数据绑定的源头窗口内的所有控件默认都会沿着视觉树向上查找直到找到它。最直接的方法是在窗口的后台代码.xaml.cs中设置。不过更符合MVVM“松耦合”思想的做法是在XAML中通过DataContext属性进行绑定或者使用像ViewModelLocator这样的模式。为了简单直观我们这次在后台代码中设置。打开MainWindow.xaml.cs。using System.Windows; using WpfLoginDemo.ViewModels; namespace WpfLoginDemo { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // 将ViewModel实例设置为窗口的数据上下文 this.DataContext new LoginViewModel(); } } }现在MainWindow的所有子控件都能访问到LoginViewModel的公共属性和命令了。接下来我们设计MainWindow.xaml。3.2 实现数据绑定与命令绑定打开MainWindow.xaml我们将使用Grid和StackPanel进行基础布局并应用绑定。Window x:ClassWpfLoginDemo.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml TitleWPF MVVM 登录演示 Height300 Width400 WindowStartupLocationCenterScreen Grid Margin20 Grid.RowDefinitions RowDefinition HeightAuto/ RowDefinition HeightAuto/ RowDefinition HeightAuto/ RowDefinition HeightAuto/ RowDefinition Height*/ RowDefinition HeightAuto/ /Grid.RowDefinitions Grid.ColumnDefinitions ColumnDefinition WidthAuto/ ColumnDefinition Width*/ /Grid.ColumnDefinitions !-- 标题 -- TextBlock Text用户登录 FontSize20 FontWeightBold Grid.ColumnSpan2 HorizontalAlignmentCenter Margin0,0,0,20/ !-- 用户名行 -- TextBlock Text用户名 VerticalAlignmentCenter Grid.Row1/ TextBox x:NameUsernameBox Grid.Row1 Grid.Column1 Margin5 Text{Binding Username, UpdateSourceTriggerPropertyChanged}/ !-- 密码行 -- TextBlock Text密 码 VerticalAlignmentCenter Grid.Row2/ PasswordBox x:NamePasswordBox Grid.Row2 Grid.Column1 Margin5 Password{Binding Password, UpdateSourceTriggerPropertyChanged}/ !-- 状态信息行 -- TextBlock Text状态 VerticalAlignmentCenter Grid.Row3/ Border Grid.Row3 Grid.Column1 Background#FFF0F0F0 CornerRadius3 Padding8 Margin5 TextBlock x:NameStatusBlock Text{Binding StatusMessage} TextWrappingWrap/ /Border !-- 按钮行 -- StackPanel OrientationHorizontal HorizontalAlignmentRight Grid.Row5 Grid.ColumnSpan2 Button Content登录 Command{Binding LoginCommand} Margin5 Padding15,5 IsDefaultTrue/ Button Content重置 Command{Binding ResetCommand} Margin5 Padding15,5/ /StackPanel /Grid /Window绑定关键点解析Text{Binding Username, UpdateSourceTriggerPropertyChanged}:{Binding Username}表示将此文本框的Text属性绑定到DataContext即我们的LoginViewModel的Username属性。UpdateSourceTriggerPropertyChanged是这里的一个重要技巧。默认情况下TextBox的绑定在控件失去焦点时才会更新源ViewModel。加上这个设置后每次用户按键ViewModel中的属性都会立即更新。这对于我们实时判断登录按钮是否可用CanExecuteLogin至关重要。Password{Binding Password, ...}:注意我们使用的是PasswordBox控件它绑定的是Password属性而不是Text。绑定原理与用户名框相同。Command{Binding LoginCommand}:将按钮的Command属性绑定到ViewModel的LoginCommand属性。当按钮被点击会自动调用命令的Execute方法。IsDefaultTrue属性将这个按钮设置为窗口的默认按钮用户按回车键即可触发登录。Text{Binding StatusMessage}:状态文本绑定到StatusMessage属性。当ViewModel中登录成功或失败时修改StatusMessage属性由于我们实现了INotifyPropertyChanged这个文本块的内容会自动更新。现在按下F5运行程序。你会看到一个登录窗口。尝试输入用户名和密码你会发现当两个输入框都为空时“登录”按钮是禁用状态。当你开始输入“登录”按钮会自动变为启用状态这得益于CanExecuteLogin逻辑和UpdateSourceTriggerPropertyChanged。输入正确的凭据admin/123456并点击登录下方会显示成功信息。点击“重置”按钮会清空输入框和状态信息。一个具备基本交互功能的MVVM登录应用已经完成了但这只是开始。一个健壮的应用还需要输入验证、更好的用户体验和可测试性。4. 进阶输入验证、依赖注入与单元测试基础功能跑通后我们可以从“能用”向“好用”和“健壮”迈进。这部分我们将探讨三个进阶主题如何为输入添加验证反馈如何用依赖注入管理ViewModel依赖以及如何为ViewModel编写单元测试。4.1 实现数据验证与UI反馈目前我们的验证逻辑只在点击“登录”时在ViewModel中进行。但更好的用户体验是在用户输入时就给予反馈。WPF提供了强大的数据验证框架通常通过IDataErrorInfo接口或INotifyDataErrorInfo接口实现。这里我们使用更现代的INotifyDataErrorInfo。首先我们创建一个支持验证的ViewModel基类ValidatableViewModel。using System.Collections.Generic; using System.ComponentModel; using System.Linq; namespace WpfLoginDemo.ViewModels { public abstract class ValidatableViewModel : INotifyPropertyChanged, INotifyDataErrorInfo { private readonly Dictionarystring, Liststring _errors new Dictionarystring, Liststring(); public event PropertyChangedEventHandler? PropertyChanged; public event EventHandlerDataErrorsChangedEventArgs? ErrorsChanged; public bool HasErrors _errors.Any(); public System.Collections.IEnumerable GetErrors(string? propertyName) { if (string.IsNullOrEmpty(propertyName) || !_errors.ContainsKey(propertyName)) return Enumerable.Emptystring(); return _errors[propertyName]; } protected void SetError(string propertyName, string errorMessage) { if (!_errors.ContainsKey(propertyName)) _errors[propertyName] new Liststring(); if (!_errors[propertyName].Contains(errorMessage)) { _errors[propertyName].Add(errorMessage); OnErrorsChanged(propertyName); } } protected void ClearError(string propertyName, string errorMessage null) { if (_errors.ContainsKey(propertyName)) { if (string.IsNullOrEmpty(errorMessage)) { _errors.Remove(propertyName); } else { _errors[propertyName].Remove(errorMessage); if (_errors[propertyName].Count 0) _errors.Remove(propertyName); } OnErrorsChanged(propertyName); } } protected virtual void OnErrorsChanged(string propertyName) { ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); } protected virtual void OnPropertyChanged([CallerMemberName] string propertyName null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }然后让LoginViewModel继承这个基类并在属性设置器中添加验证逻辑。public class LoginViewModel : ValidatableViewModel { // ... 其他字段和属性 ... public string Username { get _username; set { if (_username ! value) { _username value; OnPropertyChanged(); // 验证逻辑 ClearError(nameof(Username)); if (string.IsNullOrWhiteSpace(value)) { SetError(nameof(Username), 用户名不能为空。); } else if (value.Length 3) { SetError(nameof(Username), 用户名至少需要3个字符。); } } } } // Password属性也可以添加类似的验证如最小长度、复杂度要求等。 }最后在XAML中我们需要让控件知道如何显示这些验证错误。WPF通常使用Validation.ErrorTemplate。我们可以修改TextBox的样式或者使用一个简单的ToolTip来显示错误。这里我们修改用户名和密码的XAML添加一个TextBlock来显示错误信息。!-- 用户名行修改后 -- TextBlock Text用户名 VerticalAlignmentCenter Grid.Row1/ StackPanel Grid.Row1 Grid.Column1 TextBox x:NameUsernameBox Margin5,0,5,0 Text{Binding Username, UpdateSourceTriggerPropertyChanged, ValidatesOnNotifyDataErrorsTrue}/ TextBlock Text{Binding (Validation.Errors)[0].ErrorContent, ElementNameUsernameBox} ForegroundRed FontSize10 Margin5,2 Visibility{Binding (Validation.HasError), ElementNameUsernameBox, Converter{StaticResource BooleanToVisibilityConverter}}/ /StackPanel注意ValidatesOnNotifyDataErrorsTrue这个绑定属性它告诉绑定引擎使用我们实现的INotifyDataErrorInfo进行验证。下面的TextBlock则通过Validation附加属性获取并显示第一条错误信息。你需要为窗口或应用资源字典添加一个BooleanToVisibilityConverter。4.2 引入依赖注入在MainWindow.xaml.cs中我们直接new了一个LoginViewModel。在更复杂的应用中ViewModel可能依赖其他服务如用户认证服务、数据服务。直接new会导致紧耦合难以测试和替换。引入一个简单的依赖注入容器可以解决这个问题。我们可以使用微软的Microsoft.Extensions.DependencyInjection包。通过NuGet安装后在App.xaml.cs中配置服务。using Microsoft.Extensions.DependencyInjection; using System.Windows; using WpfLoginDemo.ViewModels; namespace WpfLoginDemo { public partial class App : Application { public IServiceProvider ServiceProvider { get; private set; } protected override void OnStartup(StartupEventArgs e) { var serviceCollection new ServiceCollection(); ConfigureServices(serviceCollection); ServiceProvider serviceCollection.BuildServiceProvider(); var mainWindow ServiceProvider.GetRequiredServiceMainWindow(); mainWindow.Show(); } private void ConfigureServices(IServiceCollection services) { // 注册ViewModels services.AddTransientLoginViewModel(); // 注册Views (Windows/UserControls) services.AddSingletonMainWindow(); // 注册其他服务如 IUserService, IDataRepository 等 // services.AddScopedIUserService, UserService(); } } }然后修改MainWindow的构造函数通过构造函数注入ViewModel。public partial class MainWindow : Window { public MainWindow(LoginViewModel viewModel) { InitializeComponent(); this.DataContext viewModel; // 从外部注入而非内部创建 } }同时需要修改App.xaml移除StartupUri。Application x:ClassWpfLoginDemo.App xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml Application.Resources /Application.Resources /Application这样做的好处是MainWindow不再负责创建LoginViewModel它只负责使用。所有依赖关系的组装都在App类中完成代码更清晰也更容易进行单元测试。4.3 为ViewModel编写单元测试MVVM的一大优势就是可测试性。ViewModel不依赖UI我们可以用单元测试框架如xUnit、NUnit或MSTest轻松测试其逻辑。假设我们使用MSTest创建一个新的单元测试项目并引用主项目。然后为LoginViewModel编写测试。using Microsoft.VisualStudio.TestTools.UnitTesting; using WpfLoginDemo.ViewModels; using WpfLoginDemo.Models; namespace WpfLoginDemo.Tests { [TestClass] public class LoginViewModelTests { [TestMethod] public void LoginCommand_CanExecute_ReturnsFalse_WhenCredentialsEmpty() { // Arrange var vm new LoginViewModel(); vm.Username ; vm.Password ; var command vm.LoginCommand as Common.RelayCommand; // Act bool canExecute command.CanExecute(null); // Assert Assert.IsFalse(canExecute); } [TestMethod] public void LoginCommand_CanExecute_ReturnsTrue_WhenCredentialsProvided() { // Arrange var vm new LoginViewModel(); vm.Username test; vm.Password pass; var command vm.LoginCommand as Common.RelayCommand; // Act bool canExecute command.CanExecute(null); // Assert Assert.IsTrue(canExecute); } [TestMethod] public void ExecuteLogin_UpdatesStatusMessage_OnSuccessfulLogin() { // Arrange var vm new LoginViewModel(); // ViewModel构造函数中创建了默认用户 admin/123456 vm.Username admin; vm.Password 123456; string initialStatus vm.StatusMessage; var command vm.LoginCommand as Common.RelayCommand; // Act command.Execute(null); // Assert Assert.AreNotEqual(initialStatus, vm.StatusMessage); StringAssert.Contains(vm.StatusMessage, 成功); } } }这些测试验证了当用户名和密码为空时登录命令不可执行。当提供了凭据时登录命令可以执行。使用正确的凭据执行登录命令后状态信息会更新为包含“成功”字样。通过编写这样的测试你可以确保业务逻辑的准确性并在未来修改代码时快速发现回归错误。这才是MVVM架构在长期项目维护中体现出的巨大价值。从创建一个简单的User模型到实现双向绑定的ViewModel再到设计交互式的View最后为应用加上验证、改进架构并使其可测试我们完成了一个小型但完整的MVVM应用闭环。这个过程可能一开始会觉得有些繁琐但一旦习惯你会发现它带来的代码组织性、可维护性和可测试性是传统事件驱动代码难以比拟的。下次当你需要处理更复杂的表单、列表或导航时这套模式将成为你得心应手的工具。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…