基于.NET6的WPF总结之MVVM

news2024/11/13 9:30:16

目录

1. DataContext数据上下文

 2. Binding绑定源

2.1 绑定实体

2.2 绑定资源

2.3 ElementName属性指明另一个控件作为数据源 

2.4 RelativeSource属性绑定一个相对的数据源

2.4.1 Self(绑定自身控件)

2.4.2 FindAncestor(找上级)

2.4.3 TemplatedParent绑定模板元素

3. Binding绑定模式

3.1 Mode属性

3.2 值改变时机

4. 前后端数据通知 

4.1 INotifyPropertyChanged接口

4.2 ObservableCollection泛型集合

5. 值转换器

5.1 IValueConverter转换器 

5.2 IMultiValueConverter多值转换器

 6. ValidationRule验证规则

 7. 命令

7.1 命令概述

7.2 ICommand接口

7.2.1 ICommand简单实现(不带参数)

7.2.2 ICommand带参数实现

7.2.3  ICommand的泛型参数实现

7.3 WPF事件转Command命令 


1. DataContext数据上下文

        DataContext是获取或设置元素参与数据绑定时的数据上下文。也就是ViewModel

下面是MVVM模型的结构图:

 

        Model作为属性存在于ViewModel中,而Model想要显示在Ul界面(View)上的,只需要将ViewModel赋值给View的DataContext(数据上下文)属性,View就可以引用ViewModel中的那些Model了。

案例如下:

首先我们定义一个ViewModel

public class PersonViewModel
{
    private string name = "Tom";
    public string Name
    {
        get { return name; }
        set
        {
            name = value;
        }
    }

    private int age = 18;
    public int Age
    {
        get { return age; }
        set
        {
            age = value;
        }
    }
}

然后再前端cs文件中将ViewModel赋值给View的数据上下文

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // 绑定上下文
        panel.DataContext = new PersonViewModel();
    }
}

最后在xaml中进行绑定就可以显示数据了(以下几种方式都可,使用后面两种不用手动绑定上下文)

<StackPanel x:Name="panel">
    <TextBlock Text="{Binding Name}" />
    <TextBlock Text="{Binding Age}" />
</StackPanel>
<StackPanel x:Name="panel">
    <StackPanel.DataContext>
        <local:PersonViewModel />
    </StackPanel.DataContext>
    <TextBlock Text="{Binding Name}" />
    <TextBlock Text="{Binding Age}" />
</StackPanel>
<Window.DataContext>
    <local:PersonViewModel />
</Window.DataContext>
<StackPanel x:Name="panel">
    <TextBlock Text="{Binding Name}" />
    <TextBlock Text="{Binding Age}" />
</StackPanel>

 

 2. Binding绑定源

2.1 绑定实体

<StackPanel x:Name="panel">
    <TextBlock Text="{Binding Name}" />
    <TextBlock Text="{Binding Age}" />
</StackPanel>

2.2 绑定资源

<Window.Resources>
    <SolidColorBrush x:Key="BackgroundBrush" Color="Red" />
</Window.Resources>
<StackPanel>
    <TextBlock Text="{Binding Source={StaticResource BackgroundBrush}, Path=Color}" />
</StackPanel>

2.3 ElementName属性指明另一个控件作为数据源 

<StackPanel x:Name="panel">
    <TextBox x:Name="txt" Text="textBox" />
    <Label Content="{Binding ElementName=txt, Path=Text}" />
</StackPanel>

2.4 RelativeSource属性绑定一个相对的数据源

Mode:表示寻找相对数据源的模式,一共有4种模式

模式说明
PreviousData允许在当前显示的数据项列表中绑定上一个数据项(不是包含数据项的控件)。
TemplatedParent引用应用了模板的元素,其中此模板中存在数据绑定元素。
Self引用正在其上设置绑定的元素,并允许你将该元素的一个属性绑定到同一元素的其他属性上。
FindAncestor引用数据绑定元素的父链中的上级。 这可用于绑定到特定类型的上级或其子类。

2.4.1 Self(绑定自身控件)

 <StackPanel>
     <TextBlock Margin="10">
             <Run Text="Hello" />
             <Run Foreground="Green" 
                  Text="{Binding RelativeSource={RelativeSource Mode=Self},
                 Path=Foreground}" />
     </TextBlock>
 </StackPanel>

 2.4.2 FindAncestor(找上级)

<StackPanel>
    <Border Margin="10">
        <Border Margin="20">
            <TextBlock>
                <Run Text="Hello" />
                <!--AncestorType:上级类型 RelativeSource:相对源 AncestorLevel:上级层级-->
                <Run Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                    AncestorType=Border,AncestorLevel=2}, Path=Margin}" />
            </TextBlock>
        </Border>
    </Border>
</StackPanel>

 2.4.3 TemplatedParent绑定模板元素

<Window.Resources>
    <ControlTemplate x:Key="MyControlTemplate" TargetType="Button">
        <Canvas>
            <Ellipse Width="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Width}"
                     Height="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Height}"
                     Fill="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Background}"/>
        </Canvas>
    </ControlTemplate>
</Window.Resources>
<StackPanel>
    <Button Width="120" Height="45" Margin="5" Background="Green"
            Template="{StaticResource MyControlTemplate}">
    </Button>
</StackPanel>

3. Binding绑定模式

3.1 Mode属性

        当一个实体的属性绑定到控件的属性之后,还需要指明这两者之间的绑定关系。这个就是Binding类的Mode属性,Mode属性是一个枚举类型。它有如下几个情况:

注:目标属性即前台UI,源属性即后台

枚举值说明
TwoWay(常用)双向绑定,即导致更改源属性或目标属性时自动更新另一方。
OneWay(常用)单向绑定,在更改绑定源(源)时更新绑定目标(目标)。
OneTime一次绑定,在应用程序启动或数据上下文更改时,更新绑定目标。
OneWayToSource在目标属性更改时,更新源属性。
Default默认绑定,文本框的默认绑定是双向的,而其他大多数属性默认为单向绑定。

3.2 值改变时机

        如果前端的值发生改变,后端的值在什么时候跟着改变。它由Binding类的UpdateSourceTrigger属性的值决定 。这个属性也是一个枚举类型。

枚举值说明
Default采用控件各自的UpdateSourceTrigger默认值。
PropertyChanged每当绑定目标属性发生更改时,都会更新绑定源。
LostFocus每当绑定目标元素失去焦点时,都会更新绑定源。
Explicit仅在调用 System.Windows.Data.BindingExpression.UpdateSource 方法时更新绑定源。

4. 前后端数据通知 

4.1 INotifyPropertyChanged接口

         INotifyPropertyChanged接口,通知客户端属性值已更改。通常情况下 我们单独创建一个类来继承该接口,目的是ViewModel、Model都可以继承这个类,不用重复去写冗余代码。

namespace WpfApp2.Common
{
    public class NotifyPropertyChanged :INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;
        /// <summary>
        /// 属性更改通知事件
        /// [CallerMemberName]添加该属性后,可以自动获取调用该方法的属性名作为参数
        /// </summary>
        /// <param name="propertyName"></param>
        protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
public class PersonViewModel : NotifyPropertyChanged
{
    private string name = "Tom";
    public string Name
    {
        get { return name; }
        set
        {
            name = value;
            OnPropertyChanged();
        }
    }
    private int age = 18;
    public int Age
    {
        get { return age; }
        set
        {
            age = value;
            OnPropertyChanged();
        }
    }
}

 4.2 ObservableCollection泛型集合

        ObservableCollection<T>泛型集合是一个动态数据集合,它可在添加、删除项目或刷新整个列表时提供通知。它继承了INotifyCollectionChanged和INotifyPropertyChanged,所以当元素数量发生变化时,前端界面也会同步发生变化。

 首先定义界面用于测试:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <ListView x:Name="myList" ItemsSource="{Binding Persons}">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}" Width="60" />
                <GridViewColumn Header="年龄" DisplayMemberBinding="{Binding Age}" Width="50" />
            </GridView>
        </ListView.View>
    </ListView>

    <StackPanel Grid.Column="1" Orientation="Vertical">
        <StackPanel Orientation="Horizontal" Height="Auto" Margin="10">
            <TextBlock Text="姓名:" />
            <TextBox x:Name="txtName" Width="200" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Height="Auto" Margin="10">
            <TextBlock Text="年龄:" />
            <TextBox x:Name="txtAge" Width="200" />
        </StackPanel>
        <Button Width="100" Height="20" Content="添加" Click="Add_Person" />
    </StackPanel>
</Grid>

 定义一个ViewModel对象,类型为ObservableCollection<PersonViewModel>:

public class BindListViewModel
{
    public ObservableCollection<PersonViewModel> Persons { get; set; } = new();
}

实现添加按钮:

private void Add_Person(object sender, RoutedEventArgs e)
{
    if (DataContext is BindListViewModel person)
    {
        PersonViewModel newPerson = new PersonViewModel()
        {
            Name = txtName.Text,
            Age = int.Parse(txtAge.Text)
        };
        person.Persons.Add(newPerson);
    }
}

运行效果:

        可以发现我们没有继承 INotifyPropertyChanged,但是依然实现说数据的通知,这是因为ObservableCollection继承了INotifyCollectionChanged和INotifyPropertyChanged。

5. 值转换器

5.1 IValueConverter转换器 

        前后端建立绑定时,定义一套自定义逻辑,让前端显示的数据与后端获取的数据建立一定的对应关系。

比如Person对象有一个年龄(Age)属性,我们在前端显示某个人的年龄时,可以根据不同的年龄,显示不同的背景颜色。这个时候,实际上是根据这个输入的整型数据返回一个不同颜色的画刷。

首先,我们定义一个根据年龄转换成不同颜色的画刷的转换器,继承IValueConverter接口,并实现它的方法:

public class AgeToBrush : IValueConverter
{
    /// <summary>
    /// 根据自定义逻辑判断,返回一个object对象给前端XAML使用
    /// </summary>
    /// <param name="value"></param>
    /// <param name="targetType"></param>
    /// <param name="parameter"></param>
    /// <param name="culture"></param>
    /// <returns></returns>
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Brush brush = Brushes.White;
        if (value != null && int.TryParse(value.ToString(), out int age))
        {

            brush = age switch
            {
                <= 10 => Brushes.Green,
                <= 20 => Brushes.Yellow,
                <= 30 => Brushes.Orange,
                _ => Brushes.Red
            };
        }
        return brush;
    }

    /// <summary>
    /// 将前端输入的数据转换成另一个对象返回给后端的数据源
    /// </summary>
    /// <param name="value"></param>
    /// <param name="targetType"></param>
    /// <param name="parameter"></param>
    /// <param name="culture"></param>
    /// <returns></returns>
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new Exception();
    }
}

讲转换器定义为资源:

<Window.Resources>
    <local1:AgeToBrush x:Key="AgeToBrush" />
</Window.Resources>

进行绑定:

<Window.Resources>
    <local1:AgeToBrush x:Key="AgeToBrush" />
</Window.Resources>
<StackPanel Margin="10">
    <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock Text="姓名:" />
        <TextBox x:Name="txtName" Width="100" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock Text="年龄:" />
        <TextBox x:Name="txtAge" Width="100"
                 Background="{Binding Converter={StaticResource AgeToBrush},ElementName=txtAge,Path=Text}" />
    </StackPanel>
</StackPanel>

5.2 IMultiValueConverter多值转换器

实例:根据年龄以及存款进行显示评价

public class MultiValueConverter : IMultiValueConverter
{
    // 第一个参数变成了values,表示它可以传入多个值
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        string result = "";
        if (int.TryParse(values[0].ToString(), out int age) && decimal.TryParse(values[1].ToString(), out decimal money))
        {
            result = (age, money) switch
            {
                (<=20,>=50000) => "优秀",
                (>=40, <=50000) => "差劲",
                _ => "未知"
            };
        }
        return result;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
<Window.Resources>
    <local1:AgeToBrush x:Key="AgeToBrush" />
    <local1:MultiValueConverter x:Key="MultiValueConverter" />
</Window.Resources>
<StackPanel Margin="10">
    <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock Text="姓名:" />
        <TextBox x:Name="txtName" Width="100" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock Text="年龄:" />
        <TextBox x:Name="txtAge" Width="100"
                 Background="{Binding Converter={StaticResource AgeToBrush},ElementName=txtAge,Path=Text}" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock Text="存款:" />
        <TextBox x:Name="txtMoney" Width="100" />
    </StackPanel>
    <StackPanel Orientation="Horizontal" Margin="5">
        <TextBlock Text="评价:"/>
        <TextBlock>
            <Run>
                <Run.Text>
                    <MultiBinding Converter="{StaticResource MultiValueConverter}">
                        <Binding ElementName="txtAge" Path="Text" />
                        <Binding ElementName="txtMoney" Path="Text" />
                    </MultiBinding>
                </Run.Text>
            </Run>
        </TextBlock>
    </StackPanel>
</StackPanel>

 6. ValidationRule验证规则

        ValidationRule是一个抽象类,提供创建自定义规则的一个方式,用于检查用户输入的有效性。

案例:判断用户输入年龄是否正常

 1. 定义验证规则,需要继承ValidationRule,并重写它的Validate方法

public class AgeValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        if (int.TryParse(value.ToString(),out int age))
        {
            if (age >=1 && age <= 100)
            {
                return new ValidationResult(true, null);
            }
        }
        return new ValidationResult(false, "请输入1-100之间");
    }
}

2. 在xaml中绑定绑定验证规则(在进行错误信息展示时,ValidationRule会把验证结果保存在AdornedElementPlaceholder的AdornedElement属性中,所以,需要利用绑定的方法去绑定下面这个路径:AdornedElement.(Validation.Errors).CurrentItem.ErrorContent)

 <Window.DataContext>
     <local3:ValidationRuleVIewModel />
 </Window.DataContext>
 <StackPanel Height="100" Width="200">
     <StackPanel Orientation="Horizontal">
         <TextBlock Text="年龄:" />
         <TextBox Width="100" Height="15">
             <!--文本绑定验证-->
             <TextBox.Text>
                 <Binding Path="Age" UpdateSourceTrigger="PropertyChanged">
                     <Binding.ValidationRules>
                         <local2:AgeValidationRule ValidatesOnTargetUpdated="True" />
                     </Binding.ValidationRules>
                 </Binding>
             </TextBox.Text>
             <!--显示错误信息-->
             <Validation.ErrorTemplate>
                 <ControlTemplate>
                     <DockPanel>
                         <TextBlock Foreground="Red" Margin="5,0,0,0" DockPanel.Dock="Right" 
                             Text="{Binding ElementName=AdornedElementPlaceholder,Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}" />
                         <AdornedElementPlaceholder x:Name="AdornedElementPlaceholder" />
                     </DockPanel>
                 </ControlTemplate>
             </Validation.ErrorTemplate>
         </TextBox>
     </StackPanel>
 </StackPanel>

3. 运行项目

 7. 命令

7.1 命令概述

WPF 中的路由命令模型可分解为四个主要概念:命令、命令源、命令目标和命令绑定:

  • 命令是要执行的操作。
  • 命令源是调用命令的对象。
  • 命令目标是在其上执行命令的对象。
  • 命令绑定是将命令逻辑映射到命令的对象。

        如上所述,命令其实就是ICommand接口的实现,命令源是调用命令的对象,这些对象一定会继承ICommandSource接口,而命令绑定就像是一座桥梁,它将命令与逻辑代码建立一种映射,这座桥梁就是CommandBinding。最后使用Binding对象将命令与命令源建立绑定。

7.2 ICommand接口

7.2.1 ICommand简单实现(不带参数)

案例:模拟登陆

1. 实现ICommand(在这里定义了一个委托,因为我们虽然命令的执行逻辑一般是写在Execute中的,但是如果说项目庞大后,Execute中就会有许多内容,所以我们定义一个委托来代替我们完成)

public class CommandDemo : ICommand
{
    private readonly Action _action; // 委托要执行的业务逻辑

    public CommandDemo(Action action)
    {
        _action = action;
    }

    public event EventHandler? CanExecuteChanged;

    /// <summary>
    /// 当前命令是否可以执行
    /// </summary>
    /// <param name="parameter"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public bool CanExecute(object? parameter)
    {
        return true;
    }

    /// <summary>
    /// 执行命令
    /// </summary>
    /// <param name="parameter"></param>
    /// <exception cref="NotImplementedException"></exception>
    public void Execute(object? parameter)
    {
        _action?.Invoke();
    }
}

 2. 建立ViewModel

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using WpfApp3.Common;

namespace WpfApp3.ViewMode
{
    public class ICommandViewModel : NotifyPropertyChanged
    {
        // 初始化命令
        public ICommandViewModel()
        {
            // 将登陆事件逻辑绑定到登陆命令
            LoginCommand = new CommandDemo(() =>
            {
                if (UserName == "1" && PassWord == "1")
                {
                    MessageBox.Show("登陆成功!");
                }
                else
                {
                    MessageBox.Show("用户名或密码错误!");
                }
            });
        }

        private string userName;

        public string UserName
        {
            get { return userName; }
            set
            {
                userName = value;
                OnPropertyChanged();
            }
        }

        private string passWord;

        public string PassWord
        {
            get { return passWord; }
            set
            {
                passWord = value;
                OnPropertyChanged();
            }
        }

        // 声明一个登陆命令
        public CommandDemo LoginCommand { get; set; }
    }

}

3. 在xaml中绑定命令

    <Window.DataContext>
        <local1:ICommandViewModel />
    </Window.DataContext>
    <StackPanel Width="300" Height="200">
        <StackPanel Orientation="Horizontal" Margin="10">
            <TextBlock Text="账号:" />
            <TextBox Width="120" Text="{Binding UserName}" />
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="10">
            <TextBlock Text="密码:" />
            <TextBox Width="120" Text="{Binding PassWord}" />
        </StackPanel>
        <Button Content="登录" Width="100" Height="30" Command="{Binding LoginCommand}" />
    </StackPanel>

4. 运行项目

 7.2.2 ICommand带参数实现

带参数的跟不带参数的实现是相似的,主需要做以下修改

1. 定义带参数的委托并初始化

private readonly Action<Object> _action2;

public CommandDemo(Action<Object> action)
{
    _action2 = action;
}

2. 在Execute方法中实现

public void Execute(object? parameter)
{
    _action?.Invoke();
    _action2?.Invoke(parameter);
}

3. 在ViewModel中声明命令并实现

public CommandDemo LoginCommand2 { get; set; }
public ICommandViewModel()
{
    // 将登陆事件逻辑绑定到登陆命令
    LoginCommand2 = new CommandDemo(p =>
    {
        MessageBox.Show(p.ToString());
    });
}

4. 在xaml中进行绑定

<Button Content="参数命令" Width="100" Height="30" Command="{Binding LoginCommand2}"
        CommandParameter="hello"/>

5. 运行项目

7.2.3  ICommand的泛型参数实现

 1. 定义泛型委托命令并初始化

 public class RelayCommand<T> : ICommand
 {
     public event EventHandler? CanExecuteChanged;
     private Action<T> _action { get; set; }
     public RelayCommand(Action<T> action)
     {
         _action = action;
     }

     public bool CanExecute(object? parameter)
     {
         return true;
     }

     public void Execute(object? parameter)
     {
         _action?.Invoke((T)parameter);
     }
 }

2. 建ViewModel,初始化委托

namespace WpfApp3.ViewMode
{
    public class ManyMultiCommandViewModel : NotifyPropertyChanged
    {
        public RelayCommand<InputCommandParam> MutilParamCommand { get; set; }
        public ManyMultiCommandViewModel()
        {
            MutilParamCommand = new RelayCommand<InputCommandParam>(p =>
            {
                MessageBox.Show($"Id:{p.Id},Name:{p.Name}");
            });
        }
    }
     
    // 声明接收多个参数的类型
    public class InputCommandParam
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

3. 创建值转换器

public class MultiCommandParamConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        InputCommandParam input = new()
        {
            Id = System.Convert.ToInt32(values[0]),
            Name = values[1].ToString()
        };
        return input;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

4. xaml中进行绑定

<Window.DataContext>
    <local1:ManyMultiCommandViewModel />
</Window.DataContext>
<StackPanel>
    <Button Content="多值传参" Width="120" Height="40"
            Command="{Binding MutilParamCommand}">
        <Button.Resources>
            <local2:MultiCommandParamConverter x:Key="MultiCommandParamConverter" />
        </Button.Resources>
        <Button.CommandParameter>
            <!--Source:直接给参数赋值-->
            <MultiBinding Converter="{StaticResource MultiCommandParamConverter}">
                <Binding Source="1" />
                <Binding Source="baobao" />
            </MultiBinding>
        </Button.CommandParameter>
    </Button>
</StackPanel>

5. 运行项目

7.3 WPF事件转Command命令 

        什么是WPF事件转Command命令?就比如我们执行某个事件后需要通过这个事件去执行其他的命令。

        这时候就需要用到WPF提供的一个组件,它的名字叫Microsoft.Xaml.Behaviors.Wpf,通过NuGet去下载。

1. 安装NuGet包

 2. 定义ViewModel,初始化命令

public class EventToCommandViewModel : NotifyPropertyChanged
{
    public CommandDemo TextClick { get; set; }

    private string text;

    public string Text
    {
        get { return text; }
        set
        {
            text = value;
            OnPropertyChanged();
        }
    }

    public EventToCommandViewModel()
    {
        TextClick = new CommandDemo(p =>
        {
            Text += $"当前时间:{DateTime.Now},参数:{p} \n";
        });
    }
}

3. 在xaml中进行绑定(鼠标点击表格后实现TextClick命令操作)

<Window.DataContext>
    <local1:EventToCommandViewModel />
</Window.DataContext>
<StackPanel>
    <TextBox TextWrapping="Wrap" Text="{Binding Path=Text}">
        <nuget1:Interaction.Triggers>
            <!--PreViewMouseDown:鼠标按下时引发的事件-->
            <nuget1:EventTrigger EventName="PreviewMouseDown">
                <nuget1:InvokeCommandAction Command="{Binding TextClick}"
                                            CommandParameter="hello">
                    
                </nuget1:InvokeCommandAction>
            </nuget1:EventTrigger>
        </nuget1:Interaction.Triggers>
    </TextBox>
</StackPanel>

4. 运行项目

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

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

相关文章

vue3获取视频时长、码率、格式等视频详细信息

前言&#xff1a; 我们在上传视频需要视频的帧数等信息的时候&#xff0c;上传组件无法直接读取帧数等信息 方法&#xff1a;通过mediainfo.js来获取视频的帧率、总帧数和视频的总时长 mediainfo.js地址&#xff0c;想详细了解的可以去看看git地址&#xff1a;https://githu…

【C++ Primer Plus习题】11.3

问题: 解答: main.cpp #include <iostream> #include <cstdlib> #include <ctime> #include "vect.h"using namespace std; using namespace VECTOR;int main() {srand(time(0));double direction;Vector step;Vector result(0.0, 0.0);unsigned …

基于 BiLSTM+Attention 实现降雨预测多变量时序分类——明日是否降雨

前言 系列专栏:【深度学习&#xff1a;算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域&#xff0c;讨论了各种复杂的深度神经网络思想&#xff0c;如卷积神经网络、循环神经网络、生成对…

uniapp解决页面跳转时,含有base64的数据丢失问题

由于url长度的限制&#xff0c;base64数据过长可能导致数据丢失&#xff0c;以至于base64图片显示不出来或者格式错误。 解决办法&#xff1a; 跳转前进行base64编码&#xff1a;encodeURIComponent 接收数据时&#xff0c;对base64进行解码&#xff1a;decodeURIComponent

【2024数模国赛赛题思路公开】国赛D题思路丨附可运行代码丨无偿自提

2024年国赛D题解题思路 问题一 【题目】 投射一枚深弹&#xff0c;潜艇中心位置的深度定位没有误差&#xff0c;两个水平坐标定位均服从正态分布。分析投弹最大命中概率与投弹落点平面坐标及定深引信引爆深度之间的关系&#xff0c;并给出使得投弹命中概率最大的投弹方案&…

One-Shot Hierarchical Imitation Learning of Compound Visuomotor Tasks

发表时间&#xff1a;25 Oct 2018 论文链接&#xff1a;https://readpaper.com/pdf-annotate/note?pdfId4500198746683498497&noteId2453372035670907392 作者单位&#xff1a;Berkeley AI Research Motivation&#xff1a;我们考虑从执行任务的人类的单个视频中学习真…

ITK-重采样

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 什么是重采样 重采样&#xff08;Resampling&#xff09; 是一种用于图像处理的技术&#xff0c;主要应用于对图像进行尺寸调整、…

【专项刷题】— 栈

1、删除字符串中的所有相邻重复项 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 使用栈进行操作&#xff0c;每次入栈的时候和栈顶元素进行比对&#xff0c;如果相同的话就弹出栈顶元素也可以用数组来模拟栈进行操作代码&#xff1a; public String removeDuplica…

基于人工智能的交通标志识别系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 交通标志识别系统是自动驾驶和智能交通的重要组成部分&#xff0c;能够帮助车辆自动识别路边的交通标志并作出相应的决策。通过使用深…

C语言-数据结构 无向图普里姆Prim算法(邻接矩阵存储)

Prim算法使用了贪心的思想&#xff0c;在算法中使用了两个数组&#xff0c;这两个数组会非常巧妙的操作整个算法的灵魂过程 lowcost的功能&#xff1a; 1.帮助算法寻找到当前距离已完成的最小生成树集合的最小的边长&#xff08;找到新边&#xff09; 2.在整个过程中记录新结…

分拣机介绍及解决方案细节

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》人俱乐部 完整版文件和更多学习资料&#xff0c;请球友到知识星球【智能仓储物流技术研习社】自行下载。 这份文件是关于交叉带式分拣机的介绍及解…

openSSL 如何降版本

文章目录 前言openSSL 如何降版本1. 卸载2. 安装新的openssl版本3. 验证 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&…

RT-Thread 使用HTTP固件下载方式进行OTA远程升级

参考资料:RT-T官网资料如下链接所示 STM32通用Bootloader (rt-thread.org) 1.app程序env配置过程 参考上述资料中"制作 app 固件"章节&#xff0c;分区大小根据自己设备而定&#xff0c;以下是我以407VET6为例设置的fal分区 notes:上述分区是由片内flash(on-chip)…

机械革命imini Pro820迷你主机评测和拆解,8845H小主机使用政府补贴仅需两千三

机械革命imini Pro820迷你主机评测和拆解&#xff0c;8845H小主机使用政府补贴仅需两千三。 最近上线了家电补贴相关的活动&#xff0c;最高可以补贴20%&#xff0c;然后就看到了这款mini主机感觉很划算就下单了&#xff0c;用来替换我旧的N5095小主机&#xff0c;当服务器用。…

电子技术基础

目录 二极管 二极管的概念二极管的整流 二极管的防反接 二极管的钳位稳压二极管 三极管 NPN型三极管PNP型三极管三极管的三种状态三极管三个极之间电流的关系 放大电路 三极管共射极放大电路分压式偏置电路静态工作点多级放大功率放大电路 运算放大器 同相比例放大器反相…

旅行商问题 | Matlab基于混合粒子群算法GA-PSO的旅行商问题TSP

目录 效果一览基本介绍建模步骤程序设计参考资料 效果一览 基本介绍 混合粒子群算法GA-PSO是一种结合了遗传算法&#xff08;Genetic Algorithm, GA&#xff09;和粒子群优化算法&#xff08;Particle Swarm Optimization, PSO&#xff09;的优化算法。在解决旅行商问题&#…

「Python数据分析」Pandas进阶,使用groupby分组聚合数据(三)

​在实际数据分析和处理过程中&#xff0c;我们可能需要灵活对分组数据进行聚合操作。这个时候&#xff0c;我们就需要用到用户自定义函数&#xff08;User-Defined Functions&#xff0c;UDFs&#xff09;。 使用用户自定义函数进行聚合 使用用户自定义函数聚合时的性能&…

联想泄露显示本月推出更便宜的Copilot Plus电脑

联想似乎准备推出新的更实惠的 Copilot Plus 电脑。可靠的爆料者Evan Blass发布了一份来自联想的新闻稿&#xff0c;详细介绍了将在本周晚些时候的IFA展会上宣布的各种Copilot Plus电脑&#xff0c;其中包括两款采用尚未公布的8核高通骁龙X Plus芯片的电脑。 这些新的高通芯片…

Qt 创建一个json数组对象写入文档并从文档读出q

void createJsonArray() { // 创建一个JSON数组 QJsonArray jsonArray; // 创建一些JSON对象并添加到数组中 for (int i 0; i < 3; i) { QJsonObject jsonObject; jsonObject["key" QString::number(i)] "value" QStri…

原点安全荣获“AutoSec Awards 安全之星”优秀汽车数据安全合规方案奖

9月3日&#xff0c;「AutoSec 2024第八届中国汽车网络安全周暨第五届智能汽车数据安全展」在上海盛大开幕。本届大会由谈思实验室和谈思汽车主办、上海市车联网协会联合主办&#xff0c;以汽车“网络数据安全、软件安全、功能安全”为主题&#xff0c;汇聚了国内外的技术专家、…