WPF 命令绑定的各种方式

引言

在WPF开发过程中,不得不学习的就是MVVM模式。但是在MVVM中又绕不开命令(Command)的使用。下面通过几种方式介绍我了解的WPF命令绑定方式。

如何使用

控件继承ICommand接口,直接使用Command

首先通过这里简单介绍Command在MVVM中的使用。

ViewModel类

// using System.ComponentModel;
// using System.Runtime.CompilerServices;

MainViewModel

 /// <summary>/// Interactive logic for MainWindow/// </summary>public class MainViewModel : INotifyPropertyChanged{public MainViewModel(){SignInCommand = new RelayCommand(() =>{MessageBox.Show($"Hello {Username},welcome to Melphily's world");});}private string username;/// <summary>/// User name/// </summary>public string Username{get { return username; }set{username = value;RaisePropertyChanged();}}/// <summary>/// Sign in command/// </summary>public ICommand SignInCommand { get; private set; }#region Notify Handlerpublic event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };public void RaisePropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged(this, new PropertyChangedEventArgs(propertyName));}#endregion}

Command类

// using System.Windows.Input;

RelayCommand

    /// <summary>/// the command without input parameter/// </summary>public class RelayCommand : ICommand{public event EventHandler CanExecuteChanged = (sender, e) => { };private Action mAction;public RelayCommand(Action action){this.mAction = action;}public bool CanExecute(object parameter){// Always allow the action can execute.return true;}public void Execute(object parameter){// Take actionmAction.Invoke();}}

View类

MainWindow.xaml

<Window x:Class="Melphily.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:Melphily"mc:Ignorable="d"Title="Melphily" Height="450" Width="800"><Grid Background="Black"><Border VerticalAlignment="Center" Width="300" Padding="20 60" Background="White" CornerRadius="5"><Border.Effect><DropShadowEffect Color="#FF924AC3"/></Border.Effect><Border.BorderBrush><LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"><GradientStop Color="#FF22B5CD" Offset="1"/><GradientStop Color="#FFB92020"/></LinearGradientBrush></Border.BorderBrush><StackPanel ><TextBlock Text="Sign In" TextAlignment="Center" FontSize="22"><TextBlock.Foreground><LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"><GradientStop Color="#FF310F89" Offset="0"/><GradientStop Color="#FF26C6EA" Offset="1"/></LinearGradientBrush></TextBlock.Foreground></TextBlock><TextBlock Text="Username" Margin="0 20 0 0"/><TextBox Text="{Binding Username}" /><Button Command="{Binding SignInCommand}" Content="Sign In" Margin="0 20 0 0"/></StackPanel></Border></Grid>
</Window>

MainWindow.xaml.cs

 /// <summary>/// Interactive logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();// Bind the Binding source to the DataContextDataContext = new MainViewModel();}}

至此,MVVM的简单应用就完成了。这里面简单实现了输入用户名登陆,在登陆(Sign In)操作时,使用了命令绑定的形式****,在ViewModel类(MainViewModel)接收命令,并做相应的处理。

运行效果:

扩展使用

上面通过较为完整的实例展示了MVVM模式中的Command绑定使用。但是一些WPF控件(例如ListBox)并不支持Command命令绑定。下面我们通过几种方式讨论如何解决,以ListBox举例。

使用InputBinding

Model

MusicItemModel.cs

public class MusicItemModel{public int Id { get; set; }public string Name { get; set; }public string Singer { get; set; }public MusicItemModel(){SelectedCommand = new RelayCommandWithParameter(p =>{MessageBox.Show($"Selected music is {Name} Singed by {Singer},the Id is {p}");});}/// <summary>/// Select command/// </summary>public ICommand SelectedCommand { get; private set; }}

Command

RelayCommandWithParameter.cs

 /// <summary>/// the command without input parameter/// </summary>public class RelayCommandWithParameter : ICommand{public event EventHandler CanExecuteChanged = (sender, e) => { };private Action<object> mActionWithParameter;public RelayCommandWithParameter(Action<object> action){this.mActionWithParameter = action;}public bool CanExecute(object parameter){// Always allow the action can execute.return true;}public void Execute(object parameter){// Take actionmActionWithParameter.Invoke(parameter);}}

View Model

MusicListViewModel.cs

 /// <summary>/// Interactive logic for MusicListPage/// </summary>public class MusicListViewModel : INotifyPropertyChanged{public MusicListViewModel(){Musics = new ObservableCollection<MusicItemModel>(){new MusicItemModel(){ Id=1, Name="甜甜的", Singer="周杰伦"},new MusicItemModel(){ Id=1, Name="说好的幸福呢", Singer="周杰伦"},new MusicItemModel(){ Id=1, Name="彩虹", Singer="周杰伦"},new MusicItemModel(){ Id=1, Name="一路向北", Singer="周杰伦"},new MusicItemModel(){ Id=1, Name="Married You", Singer="布鲁诺·马尔斯"},};}private ObservableCollection<MusicItemModel> musics;public ObservableCollection<MusicItemModel> Musics{get { return musics; }set { musics = value; RaisePropertyChanged(); }}#region Notify Handlerpublic event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };public void RaisePropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged(this, new PropertyChangedEventArgs(propertyName));}#endregion}

View

MusicListPage.xaml.cs

 /// <summary>/// Interactive logic for MusicListPage.xaml /// </summary>public partial class MusicListPage : Page{public MusicListPage(){InitializeComponent();DataContext = new MusicListViewModel();}}

MusicListPage.xaml

<Page x:Class="Melphily.MusicListPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Melphily"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"Title="MusicListPage"><Grid><ListBox ItemsSource="{Binding Musics}"><ListBox.ItemTemplate><DataTemplate ><Grid><Grid.InputBindings><MouseBinding MouseAction="LeftClick" Command="{Binding SelectedCommand}" CommandParameter="{Binding Id}"/></Grid.InputBindings><Grid.ColumnDefinitions><ColumnDefinition Width="300"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><TextBlock Grid.Column="0" Text="{Binding Name}"/><TextBlock Grid.Column="1" Text="{Binding Singer}"/></Grid></DataTemplate></ListBox.ItemTemplate></ListBox></Grid>
</Page>

命令绑定是使用Grid控件的InputBinding实现的。

<Grid.InputBindings><MouseBinding MouseAction="LeftClick" Command="{Binding SelectedCommand}" CommandParameter="{Binding Id}"/>
</Grid.InputBindings>

使用System.Windows.Interactivity.dll

呈现的内容和上面一样,只需要修改MusicListPage.xaml中的命令绑定即可。

System.Windows.Interactivity.dll这个时微软后期提供的。

1.将System.Windows.Interactivity.dll引入到项目中。

2.XAML中声明

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

3.XAML中使用

                     <i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown"><i:InvokeCommandAction Command="{Binding SelectedCommand}" CommandParameter="{Binding Id}"/></i:EventTrigger></i:Interaction.Triggers>

MusicListPage.xaml

<Page x:Class="Melphily.MusicListPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Melphily"xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"Title="MusicListPage"><Grid><ListBox ItemsSource="{Binding Musics}"><ListBox.ItemTemplate><DataTemplate ><Grid><!--<Grid.InputBindings><MouseBinding MouseAction="LeftClick" Command="{Binding SelectedCommand}" CommandParameter="{Binding Id}"/></Grid.InputBindings>--><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown"><i:InvokeCommandAction Command="{Binding SelectedCommand}" CommandParameter="{Binding Id}"/></i:EventTrigger></i:Interaction.Triggers><Grid.ColumnDefinitions><ColumnDefinition Width="300"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><TextBlock Grid.Column="0" Text="{Binding Name}"/><TextBlock Grid.Column="1" Text="{Binding Singer}"/></Grid></DataTemplate></ListBox.ItemTemplate></ListBox></Grid>
</Page>

自定义模板使用含有ICommand接口的控件传递命令

对于没有继承ICommand接口的控件,可以重定义控件的模板,并在模板中加入可以使用Command绑定的控件例如Button来实现命令的绑定。

使用上面的示例,只修改数据模板,如下所示。

MusicListPage.xaml

<Page x:Class="Melphily.MusicListPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Melphily"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"Title="MusicListPage"><Grid><ListBox ItemsSource="{Binding Musics}"><ListBox.ItemTemplate><DataTemplate ><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="300"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><TextBlock Grid.Column="0" Text="{Binding Name}"/><TextBlock Grid.Column="1" Text="{Binding Singer}"/><!-- 可以绑定命令的控件 --><Button Grid.ColumnSpan="2" Command="{Binding SelectedCommand}" CommandParameter="{Binding Id}"Foreground="Transparent" Background="Transparent" /></Grid></DataTemplate></ListBox.ItemTemplate></ListBox></Grid>
</Page>

虽然这种方式比较简单粗暴,但由于是直接在模板上面操作,所有控件是否有焦点变得非常重要了,还有就是需要隐藏控件的一些不需要显示的样式。

样式没设置好,会出现意想不到的情况。如下:

自定义模板使用用户控件(UserControl)【添加Command依赖属性】传递命令

鉴于上面自定义模板使用含有ICommand接口的控件传递命令的方式存在的一些弊端,我们可以自定义带有命令的控件来解决不便利的地方。

1.自定义CommandControl控件

CommandControl.xaml

<UserControl x:Class="Melphily.UiCore.CommandControl"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Melphily.UiCore"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"Background="Transparent">
</UserControl>

CommandControl.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;namespace Melphily.UiCore
{/// <summary>/// CommandControl.xaml 的交互逻辑/// </summary>public partial class CommandControl : UserControl{public CommandControl(){InitializeComponent();MouseLeftButtonDown += OnMouseLeftButtonDown;}private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs){if (Command != null){if (Command.CanExecute(CommandParameter)){Command.Execute(CommandParameter);}}}public static readonly DependencyProperty CommandProperty =DependencyProperty.Register("Command", typeof(ICommand),typeof(CommandControl),new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));public ICommand Command{get { return (ICommand)GetValue(CommandProperty); }set { SetValue(CommandProperty, value); }}public static readonly DependencyProperty CommandParameterProperty =DependencyProperty.Register("CommandParameter", typeof(object),typeof(CommandControl),new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));public object CommandParameter{get { return (object)GetValue(CommandParameterProperty); }set { SetValue(CommandParameterProperty, value); }}}
}

2.在模板中使用CommandControl

MusicListPage.xaml

<Page x:Class="Melphily.MusicListPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Melphily"xmlns:uicore="clr-namespace:Melphily.UiCore"xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"Title="MusicListPage"><Grid><ListBox ItemsSource="{Binding Musics}"><ListBox.ItemTemplate><DataTemplate ><uicore:CommandControl Command="{Binding SelectedCommand}"CommandParameter="{Binding Id}"><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="300"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><TextBlock Grid.Column="0" Text="{Binding Name}"/><TextBlock Grid.Column="1" Text="{Binding Singer}"/></Grid></uicore:CommandControl></DataTemplate></ListBox.ItemTemplate></ListBox></Grid>
</Page>

由于CommandControl是一个透明的控件,因此不用考虑样式的问题,可以直接使用。其次它是内容控件,因此该控件是始终存在焦点。

但是,如果将该控件作为模板(ControlTemplate/DataTemplate)时,发现无法对容器内的控件设置名称,因此,在设置样式模板时局限性会很大。

自定义模板使用自定义控件(CustomControl)【添加Command依赖属性】传递命令

鉴于用户控件带来的弊端,我们依然可以使用自定义控件(CustomControl)来解决。

创建一个自定义控件:

在项目中点击添加–》新建项

然后找到“CustomControl”或者“WPF自定义控件”,设置好名称,点击创建。

创建之后首先会存在一个对应.cs文件,然后会自动创建一个Theme(主题)文件夹,并创建一个Generic.xaml文件(通用样式文件)。

接下来我们编写自定义控件

1.加入我们的命令依赖属性和相关处理事件。

CommandControl.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;namespace Deamon.UiCore
{/// <summary>/// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。////// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。/// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根/// 元素中://////     xmlns:MyNamespace="clr-namespace:Deamon.UiCore"///////// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。/// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根/// 元素中://////     xmlns:MyNamespace="clr-namespace:Deamon.UiCore;assembly=Deamon.UiCore"////// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,/// 并重新生成以避免编译错误://////     在解决方案资源管理器中右击目标项目,然后依次单击///     “添加引用”->“项目”->[浏览查找并选择此项目]///////// 步骤 2)/// 继续操作并在 XAML 文件中使用控件。//////     <MyNamespace:CommandControl/>////// </summary>public class CommandControl : ContentControl{static CommandControl(){DefaultStyleKeyProperty.OverrideMetadata(typeof(CommandControl), new FrameworkPropertyMetadata(typeof(CommandControl)));}public CommandControl(){MouseLeftButtonDown += CommandControlCC_MouseLeftButtonDown;}private void CommandControlCC_MouseLeftButtonDown(object sender, MouseButtonEventArgs e){if (Command != null){if (Command.CanExecute(CommandParameter)){Command.Execute(CommandParameter);}}}private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs){if (Command != null){if (Command.CanExecute(CommandParameter)){Command.Execute(CommandParameter);}}}public static readonly DependencyProperty CommandProperty =DependencyProperty.Register("Command", typeof(ICommand),typeof(CommandControl),new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));public ICommand Command{get { return (ICommand)GetValue(CommandProperty); }set { SetValue(CommandProperty, value); }}public static readonly DependencyProperty CommandParameterProperty =DependencyProperty.Register("CommandParameter", typeof(object),typeof(CommandControl),new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));public object CommandParameter{get { return (object)GetValue(CommandParameterProperty); }set { SetValue(CommandParameterProperty, value); }}}
}

注释是默认加上的,是用来引导你如何设计和使用该自定义控件。默认情况下,自定义控件是继承自Control类的,因为我们的命令控件是需要包裹其它控件,所以直接继承ContentControl会减少我们对内容设计的逻辑代码。(PS:如果不这么做也可以自己添加Content属性,然后再面板中使用ContentPresenter将Content显示到界面上。)

2.在Generic.xaml文件中修改控件默认样式

Generic.xaml

<ResourceDictionaryxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:Deamon"xmlns:uicore="clr-namespace:Deamon.UiCore"><!-- 带命令绑定的内容控件 --><Style TargetType="{x:Type uicore:CommandControl}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type uicore:CommandControl}"><ContentControl><ContentPresenter/></ContentControl></ControlTemplate></Setter.Value></Setter></Style>
</ResourceDictionary>

ContentPresenter默认情况下已经将Content属性绑定到了改控件上,如果是其它控件,需要具体了解(绑定、样式、模板等)相关知识。

3.在模板中使用CommandControl

MusicListPage.xaml

<Page x:Class="Melphily.MusicListPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Melphily"xmlns:uicore="clr-namespace:Melphily.UiCore"xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"Title="MusicListPage"><Grid><ListBox ItemsSource="{Binding Musics}"><ListBox.ItemTemplate><DataTemplate ><uicore:CommandControl Command="{Binding SelectedCommand}"CommandParameter="{Binding Id}"><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="300"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><TextBlock Grid.Column="0" Text="{Binding Name}"/><TextBlock Grid.Column="1" Text="{Binding Singer}"/></Grid></uicore:CommandControl></DataTemplate></ListBox.ItemTemplate></ListBox></Grid>
</Page>

使用方式和UserControl一模一样,只是控件的定义方式不一样而已。

WPF 命令绑定的各种方式相关推荐

  1. WPF中的命令与命令绑定(二)

    WPF中的命令与命令绑定(二)                                              周银辉 在WPF中,命令(Commanding)被分割成了四个部分,分别是IC ...

  2. WPF(三) WPF 命令

    1.WPF 命令的概念 ​ WPF 区别于 WinForm,在继承WinForm熟悉的事件和委托技术之上,还提供了一套完善的命令(Command)系统.简单来说,命令是一个任务的完整封装,例如保存,复 ...

  3. MVVM 下 ContextMenu的命令绑定

    原文:MVVM 下 ContextMenu的命令绑定 由于ContextMenu不继承父级的DataContext,所以如果要绑定父级的DataContext,直接DataContext=" ...

  4. 【WPF】WPF 命令

    命令将操作的语义和发起方与其逻辑分开. 这使得多个完全不同的源可以调用相同的命令逻辑,并使得可以针对不同的目标对命令逻辑进行自定义. 命令的语义在所有的应用程序和类中是一致的,但是操作的逻辑是所作用于 ...

  5. WPF Binding(绑定)详解

    Binding概念理解: WPF为了实现了UI与数据逻辑的解耦,将UI从数据逻辑中分离出来形成Xaml文件,而UI与数据逻辑之间的联系则通过Bingding来实现.Bingding就像UI与数据逻辑之 ...

  6. 另一个绑定事件的方式 为元素绑定事件的区别

    另一个绑定事件的方式 <!DOCTYPE html> <html lang="en"> <head><meta charset=" ...

  7. wpfのuri(让你完全明白wpf的图片加载方式以及URI写法)

    原文:wpfのuri(让你完全明白wpf的图片加载方式以及URI写法) 绝对 pack WPF URI pack://application:,,,/是协议:",,,"是" ...

  8. C#使用Xamarin开发可移植移动应用(4.进阶篇MVVM双向绑定和命令绑定)附源码

    今天的学习内容? 今天我们讲讲Xamarin中的MVVM双向绑定,嗯..需要有一定的MVVM基础.,具体什么是MVVM - -,请百度,我就不多讲了 效果如下: 正文 1.简单的入门Demo 这个时间 ...

  9. js执行shell命令的几种方式(Node)

    js执行shell命令的几种方式(Node) nodejs 执行cmd或shell命令 Nodejs调用shell脚本 nodejs调用shell

最新文章

  1. 95后女程序员一下班就溜,拒绝加班!下班玩消失,不回信息!leader吐槽:95后都这么有个性吗?...
  2. 【做题】TCSRM601 Div1 500 WinterAndSnowmen——按位考虑dp
  3. Mysql 死锁过程及案例详解之显式与隐式锁Explicit Table Lock Implicit Table Lock
  4. 以CSGO为例 分析不同网络延时下FPS游戏同步的实现
  5. .NET微服务最佳实践eShopOnContainers
  6. Activiti配置实例以及Spring集成配置
  7. java中的内存一般分成几部分?
  8. 先排序,再限定记录数,然后计算指定字段的总和
  9. native与ascii互转
  10. 东航期货行情接口和交易接口(20190509)
  11. Android无界面编程之使用Service
  12. mysql必知必会心得_SQL必知必会知识总结
  13. Firebird学习(02):数据库的中文参考资料
  14. 最新雷速问卷调查系统V7.15+ASP内核开发
  15. Android 6种加载网络图片的第三方详解
  16. H5 六边形消除游戏开发 1
  17. 内农大《嵌入式基础》实验一 Shell编程
  18. 深入理解计算机系统_00
  19. github优秀项目源码汇总---Android
  20. 给hacke拨乱反正 黑客、红客、蓝客究竟是什么--来自:互联网实验室

热门文章

  1. 续集关于上次简单微信小程序制作 (快递100查询)
  2. 【网络】从日常开发说起,浅谈HTTP协议是做什么的
  3. 真假5G之争到了终结的时候,中国移动或将以低频5G击败5G厘米波
  4. 修改jupyter notebook中的字体
  5. 《python语言程序设计基础》—— 第7章
  6. Flutter 学习路线图
  7. 【考研数据】三.2022年BJTU计算机学院考研录取数据分析
  8. 关于uni-app导航栏中 中间大图标的设置
  9. 家用投影仪什么牌子好又便宜?
  10. 论文笔记 |【CVPR2021】Uformer: A General U-Shaped Transformer for Image Restoration