WPF MVVM框架

一、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);

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

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

相关文章

【nginx】client timed out和send_timeout的大小设置

websocket连接会断开&#xff0c;抓包检查后发现是中间的代理服务器nginx断开的&#xff0c;同时将后端和浏览器都断开了。将nginx日志调到debug级别后&#xff0c;有下面的断开信息。 [info] 125923#125923: *34 client timed out (110: Connection timed out) while proxyin…

python视频编辑中的蒙版技术:创意与技术相结合

在数字视频编辑的世界里&#xff0c;蒙版技术是一种强大的工具&#xff0c;它允许我们在视频帧上进行精确的编辑和效果叠加。通过蒙版&#xff0c;我们可以控制哪些部分的视频内容被显示或隐藏&#xff0c;从而创造出各种视觉效果和过渡。在本文中&#xff0c;我们将探讨如何使…

前端算法:树(力扣144、94、145、100、104题)

目录 一、树&#xff08;Tree&#xff09; 1.介绍 2.特点 3.基本术语 4.种类 二、树之操作 1.遍历 前序遍历&#xff08;Pre-order Traversal&#xff09;&#xff1a;访问根节点 -> 遍历左子树 -> 遍历右子树。 中序遍历&#xff08;In-order Traversal&#xf…

【代码审计】常见漏洞专项审计-业务逻辑漏洞审计

❤️博客主页&#xff1a; iknow181 &#x1f525;系列专栏&#xff1a; 网络安全、 Python、JavaSE、JavaWeb、CCNP &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐评论✍ 0x01 漏洞介绍 1、 原理 业务逻辑漏洞是一类特殊的安全漏洞&#xff0c;业务逻辑漏洞属于设计漏洞而非实…

Spring Boot汽车资讯:数字化时代的驾驶

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

Redis的缓存穿透、缓存雪崩、缓存击穿问题及有效解决方案

目录 一、缓存穿透 1.简介 2.解决方案 3.修改前的代码 4.修改过后的代码 二、缓存雪崩 1.简介 2.解决方案 三、缓存击穿 1.简介 2.解决方案 3.用代码来实现互斥锁来解决缓存击穿 4.用代码来实现逻辑过期解决缓存击穿 四、缓存穿透和缓存击穿的区别 一、缓存穿透 …

FPGA 第7讲 简单组合逻辑译码器

时间&#xff1a;2024.11.15 一、学习内容 1.译码器 译码是编码的逆过程&#xff0c;在编码时&#xff0c;每一种二进制代码&#xff0c;都赋予了特定的含义&#xff0c;即都表示了一个确定的信号或者对象。把代码状态的特定含义翻译出来的过程叫做译码&#xff0c;实现译码操…

如何在 Ubuntu 上安装 Jupyter Notebook

本篇文章将教你在 Ubuntu 服务器上安装 Jupyter Notebook&#xff0c;并使用 Nginx 和 SSL 证书进行安全配置。 我将带你一步步在云服务器上搭建 Jupyter Notebook 服务器。Jupyter Notebook 在数据科学和机器学习领域被广泛用于交互式编码、可视化和实验。在远程服务器上运行…

【Pikachu】XML外部实体注入实战

若天下不定&#xff0c;吾往&#xff1b;若世道不平&#xff0c;不回&#xff01; 1.XXE漏洞实战 首先写入一个合法的xml文档 <?xml version "1.0"?> <!DOCTYPE gfzq [<!ENTITY gfzq "gfzq"> ]> <name>&gfzq;</name&…

Docker安装稳定版本nginx-1.26.2

Linux 安装稳定版本nginx-1.20.2 1、下载镜像、场景配置文件目录 [rootTseng ~]# docker pull nginx:1.26.2 1.26.2: Pulling from library/nginx 2d429b9e73a6: Pull complete 40a0d865309c: Pull complete a949b43e642c: Pull complete 8a756fb620a9: Pull complete 93…

训练误差or测试误差与特征个数之间的关系--基于R语言实现

a 生成数据集&#xff0c;数据由 Y X β ϵ YX\beta\epsilon YXβϵ产生&#xff0c;其中 p 20 &#xff0c; n 1000 p20&#xff0c;n1000 p20&#xff0c;n1000 #way1 set.seed(1) p 20 n 1000 x matrix(rnorm(n*p), n, p) B rnorm(p) B[3] 0 B[4] 0 B[9] 0 B[19…

kafka基础

文章目录 一、Kafka入门1.1、JMS1.2、生产者-消费者模式1.3、ZooKeeper 二、kafka基础架构2.1、producer2.2、kafka cluster2.2.1、broker2.2.2、Controller2.2.3、Topic2.2.4、Partition2.2.5、Replication2.2.6、Leader & Follower 2.3、consumer 一、Kafka入门 Kafka是一…

HarmonyOs鸿蒙开发实战(10)=>状态管理-对象数组的属性数据变更刷新UI,基于@Observed 和@ObjectLink装饰器

1.条件:基于HarmonyOs5.0.0版本. 2.功能要求&#xff1a;横向列表中每个景点的名称&#xff08;eg: 第二项 “灵隐寺” &#xff09;, 在通过天气接口拿到对应天气后&#xff0c;拼接到名称后面 > 变成&#xff08;“灵隐寺” 天气&#xff09;&#xff09; 3.老规矩先看…

诡异错误:返回给前端的id被前端自动修改

使用mybatis-plus生成的id&#xff0c;使用雪花算法&#xff0c;是一个long类型的id。 当调用list接口返回给前端后&#xff0c;接口显示数据正常&#xff0c;但是界面上的id不对&#xff0c;多了好几个0&#xff0c;数据都是以0结尾。 由于前端使用vue编写&#xff0c;我不太会…

Django5 2024全栈开发指南(一):框架简介、环境搭建与项目结构

目录 一、Python Web框架要点二、Django流程2.1 Django介绍2.1.1 简介2.1.2 特点2.1.3 MVT模式2.1.4 Django新特性2.1.5 Django学习资料 2.2 搭建Django框架开发环境2.2.1 安装Python语言环境2.2.2 安装Django框架 2.3 创建Django项目2.4 Pycharm创建项目2.5 初试Django52.5.1 …

大模型研究报告 | 2024年中国金融大模型产业发展洞察报告|附34页PDF文件下载

随着生成算法、预训练模型、多模态数据分析等AI技术的聚集融合&#xff0c;AIGC技术的实践效用迎来了行业级大爆发。通用大模型技术的成熟推动了新一轮行业生产力变革&#xff0c;在投入提升与政策扶植的双重作用下&#xff0c;以大模型技术为底座、结合专业化金融能力的金融大…

深入内核讲明白Android Binder【一】

深入内核讲明白Android Binder【一】 前言一、Android Binder应用编写概述二、基于C语言编写Android Binder跨进程通信Demo0. Demo简介1. 服务的管理者server_manager.c2. Binder服务端代码实现 test_service.c2.1 实现思路2.2 完整实现代码 3. Binder客户端代码实现 test_clie…

新一代API开发工具,让API调试更快 更简单

新一代API开发工具 代理调试 请求测试一站式解决方案 Reqable Fiddler Charles Postman, 让API调试更快 &#x1f680; 更简单 &#x1f44c; 直接上下载地址 根据系统,下载对应的版本即可 https://reqable.com/zh-CN/download/

LVGL-从入门到熟练使用

LVGL简介 LVGL&#xff08; Light and Versatile Graphics Library &#xff09;是一个轻量、多功能的开源图形库。 1、丰富且强大的模块化图形组件&#xff1a;按钮 、图表 、列表、滑动条、图片等 2、高级的图形引擎&#xff1a;动画、抗锯齿、透明度、平滑滚动、图层混合等…

从视频帧生成点云数据、使用PointNet++模型提取特征,并将特征保存下来的完整实现。

文件地址 https://github.com/yanx27/Pointnet_Pointnet2_pytorch?spm5176.28103460.0.0.21a95d27ollfze Pointnet_Pointnet2_pytorch\log\classification\pointnet2_ssg_wo_normals文件夹改名为Pointnet_Pointnet2_pytorch\log\classification\pointnet2_cls_ssg "E:…