WPF 上位机开发模板

WPF 上位机开发模板

WPF上位机开发模板,集成了基础操作菜单、海康视觉实时图像界面、串口通讯、网口通讯、主流PLC通讯、数据存储、图片存储、参数配置、权限管理、第三方webapi接口接入、数据追溯与查询等功能。

一、项目结构

WpfSupervisor/
├── Models/                  # 数据模型
│   ├── DeviceModels.cs
│   ├── ImageModel.cs
│   ├── LogModel.cs
│   ├── ParameterModel.cs
│   └── UserModel.cs
├── Services/                # 服务层
│   ├── Communication/
│   │   ├── ComService.cs
│   │   ├── EthernetService.cs
│   │   ├── PlcService.cs
│   │   └── WebApiService.cs
│   ├── Database/
│   │   ├── DatabaseService.cs
│   │   └── ImageStorage.cs
│   ├── HikVision/
│   │   └── HikVisionService.cs
│   ├── Security/
│   │   ├── AuthService.cs
│   │   └── PermissionService.cs
│   └── Utility/
│       ├── ConfigManager.cs
│       └── Logger.cs
├── ViewModels/              # 视图模型
│   ├── MainViewModel.cs
│   ├── CommunicationViewModel.cs
│   ├── ImageViewModel.cs
│   ├── ParameterViewModel.cs
│   └── UserViewModel.cs
├── Views/                   # 视图
│   ├── MainWindow.xaml
│   ├── CommunicationView.xaml
│   ├── ImageView.xaml
│   ├── ParameterView.xaml
│   └── LoginView.xaml
├── Helpers/                 # 辅助类
│   ├── RelayCommand.cs
│   └── EnumExtensions.cs
└── App.xaml.cs              # 应用程序入口

二、核心代码实现

1. 数据模型 (Models/)

// DeviceModels.cs
public class PlcDevice
{public string Id { get; set; }public string Name { get; set; }public string IpAddress { get; set; }public int Port { get; set; }public string Protocol { get; set; } // Modbus, S7, etc.
}public class SerialDevice
{public string Id { get; set; }public string Name { get; set; }public string PortName { get; set; }public int BaudRate { get; set; }public Parity Parity { get; set; }public int DataBits { get; set; }public StopBits StopBits { get; set; }
}// ImageModel.cs
public class CapturedImage
{public string Id { get; set; }public byte[] ImageData { get; set; }public DateTime CaptureTime { get; set; }public string DeviceId { get; set; }public string FilePath { get; set; }
}// LogModel.cs
public class SystemLog
{public string Id { get; set; }public DateTime Timestamp { get; set; }public string Level { get; set; } // Info, Warning, Errorpublic string Message { get; set; }public string UserId { get; set; }
}// ParameterModel.cs
public class SystemParameter
{public string Id { get; set; }public string Key { get; set; }public string Value { get; set; }public string Description { get; set; }public string Category { get; set; }
}// UserModel.cs
public class User
{public string Id { get; set; }public string Username { get; set; }public string PasswordHash { get; set; }public string FullName { get; set; }public string Role { get; set; } // Admin, Operator, etc.public DateTime LastLogin { get; set; }
}

2. 服务层 (Services/)

2.1 通信服务
// ComService.cs
public class ComService : IDisposable
{private SerialPort _serialPort;public event Action<string> DataReceived;public bool IsOpen => _serialPort?.IsOpen ?? false;public void Open(SerialDevice device){_serialPort = new SerialPort(device.PortName, device.BaudRate, device.Parity, device.DataBits, device.StopBits);_serialPort.DataReceived += (s, e) => {try{var data = _serialPort.ReadExisting();DataReceived?.Invoke(data);}catch (Exception ex){Logger.LogError($"串口数据接收错误: {ex.Message}");}};_serialPort.Open();}public void Close() => _serialPort?.Close();public void Send(string data) => _serialPort?.Write(data);public void Dispose() => Close();
}// EthernetService.cs
public class EthernetService : IDisposable
{private TcpClient _tcpClient;private NetworkStream _stream;public event Action<string> DataReceived;public bool IsConnected => _tcpClient?.Connected ?? false;public async Task ConnectAsync(string ipAddress, int port){_tcpClient = new TcpClient();await _tcpClient.ConnectAsync(ipAddress, port);_stream = _tcpClient.GetStream();_ = ReceiveDataAsync();}private async Task ReceiveDataAsync(){try{var buffer = new byte[1024];while (_stream != null && _stream.CanRead){int bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length);var data = Encoding.ASCII.GetString(buffer, 0, bytesRead);DataReceived?.Invoke(data);}}catch (Exception ex){Logger.LogError($"以太网数据接收错误: {ex.Message}");}}public void Send(string data){if (_stream == null || !_stream.CanWrite) return;var bytes = Encoding.ASCII.GetBytes(data);_stream.Write(bytes, 0, bytes.Length);}public void Disconnect() => _tcpClient?.Close();public void Dispose(){Disconnect();_stream?.Close();}
}// PlcService.cs (使用S7.Net库示例)
public class PlcService : IDisposable
{private S7.Net.PLC _plc;public event Action<string> DataReceived;public bool IsConnected => _plc?.IsConnected ?? false;public async Task ConnectAsync(PlcDevice device){_plc = new S7.Net.PLC(device.Protocol == "S7" ? S7.Net.CpuType.S71200 : S7.Net.CpuType.S7300, device.IpAddress, device.Port);await Task.Run(() => _plc.Open());}public async Task<T> ReadAsync<T>(string address){if (!IsConnected) throw new InvalidOperationException("PLC未连接");return await Task.Run(() => (T)_plc.Read(address));}public async Task WriteAsync<T>(string address, T value){if (!IsConnected) throw new InvalidOperationException("PLC未连接");await Task.Run(() => _plc.Write(address, value));}public void Dispose() => _plc?.Close();
}// WebApiService.cs
public class WebApiService
{private readonly HttpClient _httpClient;private readonly string _baseUrl;public WebApiService(string baseUrl){_baseUrl = baseUrl.EndsWith("/") ? baseUrl : baseUrl + "/";_httpClient = new HttpClient();}public async Task<T> GetAsync<T>(string endpoint){var response = await _httpClient.GetAsync(_baseUrl + endpoint);response.EnsureSuccessStatusCode();return await response.Content.ReadAsAsync<T>();}public async Task PostAsync<T>(string endpoint, object data){var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");var response = await _httpClient.PostAsync(_baseUrl + endpoint, content);response.EnsureSuccessStatusCode();}
}
2.2 数据库服务
 
// DatabaseService.cs
public class DatabaseService
{private readonly string _connectionString;private SQLiteConnection _connection;public DatabaseService(string dbPath){_connectionString = $"Data Source={dbPath};Version=3;";InitializeDatabase();}private void InitializeDatabase(){_connection = new SQLiteConnection(_connectionString);_connection.Open();// 创建表ExecuteNonQuery(@"CREATE TABLE IF NOT EXISTS SystemLogs (Id TEXT PRIMARY KEY,Timestamp TEXT,Level TEXT,Message TEXT,UserId TEXT);CREATE TABLE IF NOT EXISTS SystemParameters (Id TEXT PRIMARY KEY,Key TEXT,Value TEXT,Description TEXT,Category TEXT);CREATE TABLE IF NOT EXISTS Users (Id TEXT PRIMARY KEY,Username TEXT,PasswordHash TEXT,FullName TEXT,Role TEXT,LastLogin TEXT);");}public void ExecuteNonQuery(string sql, params object[] parameters){using (var cmd = new SQLiteCommand(sql, _connection)){for (int i = 0; i < parameters.Length; i++){cmd.Parameters.AddWithValue($"@p{i}", parameters[i]);}cmd.ExecuteNonQuery();}}public T ExecuteScalar<T>(string sql, params object[] parameters){using (var cmd = new SQLiteCommand(sql, _connection)){for (int i = 0; i < parameters.Length; i++){cmd.Parameters.AddWithValue($"@p{i}", parameters[i]);}return (T)cmd.ExecuteScalar();}}public DataTable ExecuteQuery(string sql, params object[] parameters){using (var cmd = new SQLiteCommand(sql, _connection)){for (int i = 0; i < parameters.Length; i++){cmd.Parameters.AddWithValue($"@p{i}", parameters[i]);}var adapter = new SQLiteDataAdapter(cmd);var table = new DataTable();adapter.Fill(table);return table;}}public void Dispose(){_connection?.Close();}
}// ImageStorage.cs
public class ImageStorage
{private readonly string _imageFolderPath;private readonly DatabaseService _dbService;public ImageStorage(string folderPath, DatabaseService dbService){_imageFolderPath = folderPath;Directory.CreateDirectory(_imageFolderPath);_dbService = dbService;}public async Task SaveImageAsync(CapturedImage image){// 保存到数据库var imageId = Guid.NewGuid().ToString();var parameters = new object[]{imageId,image.ImageData != null ? Convert.ToBase64String(image.ImageData) : null,image.CaptureTime.ToString("o"),image.DeviceId,image.FilePath};_dbService.ExecuteNonQuery(@"INSERT INTO Images (Id, Data, CaptureTime, DeviceId, FilePath)VALUES (@p0, @p1, @p2, @p3, @p4);", parameters);// 保存文件if (image.ImageData != null){var filePath = Path.Combine(_imageFolderPath, $"{imageId}.jpg");await File.WriteAllBytesAsync(filePath, image.ImageData);// 更新数据库中的文件路径_dbService.ExecuteNonQuery(@"UPDATE Images SET FilePath = @p0 WHERE Id = @p1;", filePath, imageId);}}public async Task<CapturedImage> GetImageAsync(string id){var row = _dbService.ExecuteQuery("SELECT * FROM Images WHERE Id = @p0;", id).Rows[0];return new CapturedImage{Id = row["Id"].ToString(),ImageData = row["Data"] != DBNull.Value ? Convert.FromBase64String(row["Data"].ToString()) : null,CaptureTime = DateTime.Parse(row["CaptureTime"].ToString()),DeviceId = row["DeviceId"].ToString(),FilePath = row["FilePath"]?.ToString()};}
}
2.3 海康视觉服务
// HikVisionService.cs
public class HikVisionService
{private readonly HttpClient _httpClient;private readonly string _baseUrl;private readonly string _username;private readonly string _password;public event Action<byte[]> ImageReceived;public HikVisionService(string baseUrl, string username, string password){_baseUrl = baseUrl.EndsWith("/") ? baseUrl : baseUrl + "/";_username = username;_password = password;_httpClient = new HttpClient();// 登录Login();}private void Login(){var loginData = new Dictionary<string, string>{{"action", "login"},{"user", _username},{"password", _password}};var content = new FormUrlEncodedContent(loginData);var response = _httpClient.PostAsync(_baseUrl + "login", content).Result;response.EnsureSuccessStatusCode();}public async Task StartRealTimeImage(){// 启动实时图像流var streamResponse = await _httpClient.GetAsync($"{_baseUrl}stream");streamResponse.EnsureSuccessStatusCode();var stream = await streamResponse.Content.ReadAsStreamAsync();// 处理图像流数据using (var reader = new BinaryReader(stream)){while (true){// 实际实现需要解析海康威视的流协议// 这里简化处理var buffer = reader.ReadBytes(1024);if (buffer.Length > 0){// 解码图像数据var imageData = DecodeHikVisionImage(buffer);ImageReceived?.Invoke(imageData);}}}}private byte[] DecodeHikVisionImage(byte[] buffer){// 实际实现需要根据海康威视的图像编码格式解码// 这里简化处理,直接返回原始数据return buffer;}
}
2.4 安全服务
 
// AuthService.cs
public class AuthService
{private readonly DatabaseService _dbService;public AuthService(DatabaseService dbService){_dbService = dbService;}public async Task<User> LoginAsync(string username, string password){var userRow = _dbService.ExecuteQuery("SELECT * FROM Users WHERE Username = @p0;", username).Rows[0];var user = MapUserFromRow(userRow);// 验证密码if (VerifyPassword(password, user.PasswordHash)){user.LastLogin = DateTime.UtcNow.ToString("o");await UpdateUserAsync(user);return user;}return null;}private bool VerifyPassword(string inputPassword, string storedHash){// 实际实现应使用安全的密码哈希验证// 这里简化处理return inputPassword == storedHash; }public async Task<User> GetUserAsync(string userId){var row = _dbService.ExecuteQuery("SELECT * FROM Users WHERE Id = @p0;", userId).Rows[0];return MapUserFromRow(row);}private User MapUserFromRow(DataRow row){return new User{Id = row["Id"].ToString(),Username = row["Username"].ToString(),PasswordHash = row["PasswordHash"].ToString(),FullName = row["FullName"].ToString(),Role = row["Role"].ToString(),LastLogin = row["LastLogin"]?.ToString()};}private async Task UpdateUserAsync(User user){_dbService.ExecuteNonQuery(@"UPDATE Users SET LastLogin = @p0 WHERE Id = @p1;",user.LastLogin, user.Id);}
}// PermissionService.cs
public class PermissionService
{private readonly DatabaseService _dbService;public PermissionService(DatabaseService dbService){_dbService = dbService;}public async Task<bool> HasPermissionAsync(string userId, string permission){// 从数据库查询用户权限var hasPermission = await _dbService.ExecuteScalarAsync<bool>("SELECT COUNT(*) > 0 FROM UserPermissions WHERE UserId = @p0 AND Permission = @p1;",userId, permission);return hasPermission;}public async Task<IEnumerable<string>> GetUserPermissionsAsync(string userId){var permissions = await _dbService.ExecuteQueryAsync("SELECT Permission FROM UserPermissions WHERE UserId = @p0;", userId);return permissions.Select(r => r["Permission"].ToString());}
}

3. 视图模型 (ViewModels/)

// MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{private readonly IEventAggregator _eventAggregator;private readonly AuthService _authService;private readonly PermissionService _permissionService;private object _currentView;private User _currentUser;public object CurrentView{get => _currentView;set { _currentView = value; OnPropertyChanged(); }}public User CurrentUser{get => _currentUser;private set { _currentUser = value; OnPropertyChanged(); }}public ICommand LoginCommand { get; }public ICommand LogoutCommand { get; }public MainViewModel(IEventAggregator eventAggregator,AuthService authService,PermissionService permissionService){_eventAggregator = eventAggregator;_authService = authService;_permissionService = permissionService;LoginCommand = new RelayCommand(Login);LogoutCommand = new RelayCommand(Logout, CanLogout);}private async void Login(){// 实际实现应显示登录对话框var loginView = new LoginView();if (loginView.ShowDialog() == true){var user = await _authService.LoginAsync(loginView.Username, loginView.Password);if (user != null){CurrentUser = user;CurrentView = new ShellViewModel(_eventAggregator, user).View;_eventAggregator.Publish(new UserLoggedInEvent(user));}}}private void Logout(){CurrentUser = null;CurrentView = new LoginView();_eventAggregator.Publish(new UserLoggedOutEvent());}private bool CanLogout() => CurrentUser != null;public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}
}// CommunicationViewModel.cs
public class CommunicationViewModel : INotifyPropertyChanged
{private readonly ComService _comService;private readonly EthernetService _ethernetService;private readonly PlcService _plcService;private readonly WebApiService _webApiService;private SerialDevice _selectedSerialDevice;private PlcDevice _selectedPlcDevice;public ObservableCollection<SerialDevice> SerialDevices { get; } = new();public ObservableCollection<PlcDevice> PlcDevices { get; } = new();public SerialDevice SelectedSerialDevice{get => _selectedSerialDevice;set { _selectedSerialDevice = value; OnPropertyChanged();OpenSerialPort();}}public PlcDevice SelectedPlcDevice{get => _selectedPlcDevice;set { _selectedPlcDevice = value; OnPropertyChanged();ConnectPlc();}}public ICommand RefreshDevicesCommand { get; }public ICommand SendSerialCommand { get; }public ICommand ReadPlcCommand { get; }public ICommand WritePlcCommand { get; }public CommunicationViewModel(ComService comService,EthernetService ethernetService,PlcService plcService,WebApiService webApiService){_comService = comService;_ethernetService = ethernetService;_plcService = plcService;_webApiService = webApiService;RefreshDevicesCommand = new RelayCommand(RefreshDevices);SendSerialCommand = new RelayCommand(SendSerialData, CanSendSerial);ReadPlcCommand = new RelayCommand(ReadPlcData, CanReadPlc);WritePlcCommand = new RelayCommand(WritePlcData, CanWritePlc);LoadDevices();}

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

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

相关文章

浏览器插件,提示:此扩展程序未遵循 Chrome 扩展程序的最佳实践,因此已无法再使用

1、发现的问题如下&#xff1a; 如果你是比较新的 Chrome 135.0.7049.42&#xff08;含&#xff09;以上版本的话&#xff0c;可以通过修改 chorme://flags 来彻底解决。 2、在浏览器分别输入两个地址&#xff1a; chrome://flags/#extension-manifest-v2-deprecation-disable…

【原创】从s3桶将对象导入ES建立索引,以便快速查找文件

总体功能&#xff1a; 这段程序的作用是&#xff1a; 从指定的S3桶中读取所有对象的元数据&#xff08;文件名、大小、最后修改时间、存储类型、ETag等&#xff09;&#xff0c;并把这些信息写入到Elasticsearch&#xff08;ES&#xff09;中&#xff0c;建立索引&#xff0c…

git 查看用户信息

在 Git 中查看用户信息是一项常见的任务&#xff0c;可以帮助你确认当前仓库的配置或全局的 Git 配置是否正确设置。你可以通过多种方式来查看这些信息。 查看全局用户信息 全局用户信息是应用于所有 Git 仓库的默认设置。要查看全局用户信息&#xff0c;可以使用以下命令&am…

制作JDK17 arm64基础镜像,解决字体安装问题

1、下载jdk17 arm64的安装包 官网下载地址 2、编写Dockerfile 图形验证码生成需要使用到相关字体&#xff0c;所以基础镜像把字体相关也安装上。 # 基础镜像 FROM arm64v8/centos:8.4.2105MAINTAINER hqh# 换源 RUN sed -i s|^mirrorlist|#mirrorlist|g /etc/yum.repos.d/…

人工智能数学基础(三):微积分初步

微积分作为数学的重要分支&#xff0c;为人工智能的发展提供了坚实的理论基础。从理解数据的变化趋势到优化模型参数&#xff0c;微积分的应用贯穿其中。本文将深入探讨微积分的核心概念&#xff0c;并结合 Python 编程实例&#xff0c;助力大家轻松掌握这些关键知识点。资源绑…

区块链密码学核心

文章目录 概要1. 基础密码学哈希函数&#xff08;Hash Function&#xff09;对称加密与非对称加密数字签名&#xff08;Digital Signature&#xff09;密钥管理 2. 区块链专用密码学技术零知识证明&#xff08;Zero-Knowledge Proof, ZKP&#xff09;同态加密&#xff08;Homom…

Java后端开发day39--方法引用

&#xff08;以下内容全部来自上述课程&#xff09; 1.1 含义 把已经有的方法拿过来用&#xff0c;当作函数式接口中抽象方法的方法体。 已经有的方法&#xff1a;可以是Java自己写的&#xff0c;也可以是第三方的。 示例语句&#xff1a; &#xff1a;&#xff1a;是方法引…

目前市面上知名的数据采集器

程序员爱自己动手打造一切&#xff0c;但这样离钱就会比较远。 市面上知名的数据采集工具 数据采集工具&#xff08;也称为网络爬虫或数据抓取工具&#xff09;在市场上有很多选择&#xff0c;以下是目前比较知名和广泛使用的工具分类介绍&#xff1a; 一、开源免费工具 Scra…

TP5兼容达梦国产数据库

1.首先数据库安装&#xff0c;部署时需配置大小写不敏感 2.安装PHP达梦扩展&#xff0c;一定要是对应版本&#xff08;兼容操作系统&#xff09;的扩展&#xff0c;否则会出现各种报错。参考官方文档&#xff1a;https://eco.dameng.com/document/dm/zh-cn/app-dev/php_php_new…

《解锁图像“高清密码”:超分辨率重建之路》

在图像的世界里&#xff0c;高分辨率意味着更多细节、更清晰的画面&#xff0c;就像用高清望远镜眺望远方&#xff0c;一切都纤毫毕现。可现实中&#xff0c;我们常被低分辨率图像困扰&#xff0c;模糊的监控画面、老旧照片里难以辨认的面容……不过别担心&#xff0c;图像超分…

整合 CountVectorizer 和 TfidfVectorizer 绘制词云图

本文分别整合 CountVectorizer 和 TfidfVectorizer 绘制词云图 ✨ CountVectorizer CountVectorizer 是 scikit-learn 中用于 文本特征提取 的一个工具&#xff0c;它的主要作用是将一组文本&#xff08;文本集合&#xff09;转换为词频向量&#xff08;Bag-of-Words&#xf…

Linux 用户管理

用户管理是 Linux 系统管理中的重要组成部分&#xff0c;它涉及到用户和用户组的创建、删除、修改以及权限分配等操作。以下是关于用户和用户组管理的详细说明&#xff1a; 一、用户和用户组的概念 &#xff08;一&#xff09;用户&#xff08;User&#xff09; 用户是系统中…

【HTTP/2和HTTP/3的应用现状:看不见的革命】

HTTP/2和HTTP/3的应用现状&#xff1a;看不见的革命 实际上&#xff0c;HTTP/2和HTTP/3已经被众多著名网站广泛采用&#xff0c;只是这场革命对普通用户来说是"无形"的。让我们揭开这个技术变革的真相。 著名网站的HTTP/2和HTTP/3采用情况 #mermaid-svg-MtfrNDo5DG…

青少年编程与数学 02-018 C++数据结构与算法 16课题、贪心算法

青少年编程与数学 02-018 C数据结构与算法 16课题、贪心算法 一、贪心算法的基本概念定义组成部分 二、贪心算法的工作原理三、贪心算法的优点四、贪心算法的缺点五、贪心算法的应用实例&#xff08;一&#xff09;找零问题问题描述&#xff1a;贪心策略&#xff1a;示例代码&a…

UE5 Set actor Location和 Set World Location 和 Set Relative Location 的区别

在 Unreal Engine 的蓝图里&#xff0c;SetRelativeLocation、SetWorldLocation 和 SetActorLocation 三个节点虽然都能改变物体位置&#xff0c;但作用对象和坐标空间&#xff08;Coordinate Space&#xff09;不同&#xff1a; 1. SetActorLocation 作用对象&#xff1a;整个…

VINS-FUSION:跑通手机录制数据

文章目录 &#x1f4da;简介&#x1f680;手机录制数据&#x1f680;跑通数据&#x1f527;启动rviz&#x1f527;启动配置&#x1f527;播放rosbag&#x1f3af;跑通结果 &#x1f4da;简介 利用智能手机的 摄像头IMU 采集数据&#xff0c;并在 VINS-Fusion&#xff08;视觉惯…

Spring AI在大模型领域的趋势场景题深度解析

Spring AI在大模型领域的趋势场景题深度解析 在互联网大厂Java求职者的面试中&#xff0c;经常会被问到关于Spring AI在大模型领域的趋势场景的相关问题。本文通过一个故事场景来展示这些问题的实际解决方案。 第一轮提问 面试官&#xff1a;马架构&#xff0c;欢迎来到我们…

MySQL数据库全面详解:从基础到高级应用

一、数据存储概述 在计算机系统中&#xff0c;数据可以存储在多种形式中&#xff1a; 变量&#xff1a;程序中最基本的数据存储单元 元组&#xff1a;不可变的序列类型&#xff0c;常用于函数返回多个值 列表&#xff1a;有序可变集合&#xff0c;可存储不同类型元素 字典&…

Redux和MobX有什么区别

Redux 和 MobX 都是用于 React 应用的全局状态管理库&#xff0c;但它们在设计理念、使用方式和适用场景等方面存在明显的区别&#xff0c;下面为你详细分析&#xff1a; 1. 设计理念 Redux&#xff1a;基于 Flux 架构&#xff0c;遵循单向数据流和纯函数式编程的理念。状态是…

WPF实现类似Microsoft Visual Studio2022界面效果及动态生成界面技术

WPF实现类似VS2022界面效果及动态生成界面技术 一、实现类似VS2022界面效果 1. 主窗口布局与主题 <!-- MainWindow.xaml --> <Window x:Class"VsStyleApp.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x…