zoukankan      html  css  js  c++  java
  • WPF学习(2)XAML

    XAML(eXtensible Application Markup Language,可扩展应用程序标记语言)是一种声明式的编程语言,遵循XML的语法。WPF使用XAML来设计UI具有易用性、高效性等特点。易用性主要表现在设计师在不需懂逻辑代码的情况下就可以使用Expression Blend设计出优雅的界面以及一些动画效果。我们将分两个大的部分来说明,一个是XAML基本认识,另一个是XAML中最重要的X命名空间。

    1.XAML基本认识

    下面我们新建一个WPF应用程序,看看Xaml页面有哪些基本的东西

     

    从上图我们可以看出Xaml有一个根元素Window,根元素下有一个子元素Grid,表示出清晰的层级关系。Window还有一些属性(Attribute):

    x:Class属于x命名空间的内容,我们后面详述

    xmlns是XML Namespace的缩写,等号后面看起来像一个URL,其实是采用硬编码(Hard Code)的形式来引用一些.net命名空间。

            如果要引用多个命名空间,为了区别,我们可以用标识前缀来限定修饰,如xmlns:x

    Tilte、Height、Width属性用来修饰Window

    1.1集合语法

    <Window x:Class="XamlDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Ellipse Width="80" Height="80">
                <Ellipse.Fill>
                    <RadialGradientBrush GradientOrigin="0.5,0.5">
                        <RadialGradientBrush.GradientStops>
                            <GradientStop Offset="0" Color="Red" />
                            <GradientStop Offset="0.6" Color="Green" />
                            <GradientStop Offset="1" Color="Blue" />
                        </RadialGradientBrush.GradientStops>
                    </RadialGradientBrush>
                </Ellipse.Fill>
            </Ellipse>
        </Grid>
    </Window>
    View Code

     RadialGradientBrush.GradientStops是一个GradientStop集合,我们可以像上面那样来编写

    1.2Xaml内容属性

    XAML 指定了一个语言功能,通过该功能,一个类可以指定它的一个且仅一个属性为 XAML 内容属性。 该对象元素的子元素用于设置该内容属性的值。 换言之,仅对内容属性而言,您可以在 XAML 标记中设置该属性时省略属性元素,并在标记中生成更直观的父级/子级形式。本文刚开始给了一张图片,在Window下放了一个Grid,其实它的完整写法应该是这样:

    <Window x:Class="XamlDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Window.Content>
            <Grid>
                
            </Grid>
        </Window.Content>
    </Window>
    View Code

     由上面代码知道,Content属性是Window的默认内容属性,因此你可以直接省略它,这样看起来更直观。

    1.3文本内容

    有少量 XAML 元素可直接将文本作为其内容来处理。 若要实现此功能,必须满足以下条件之一:

         1.类必须声明一个内容属性,并且该内容属性必须是可赋值给字符串的类型(该类型可以是 Object)。

         2. 类型必须声明一个类型转换器,该类型转换器将文本内容用作其初始化文本。 例如,<Brush>Blue</Brush> 这种情况实际上并不常见

         3.类型必须为已知的 XAML 语言基元

    我们来举个例子看看:

    <Window x:Class="XamlDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Window.Content>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="100" />
                    <RowDefinition Height="5" />
                    <RowDefinition Height="100" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100" />
                    <ColumnDefinition Width="5" />
                    <ColumnDefinition Width="100" />
                </Grid.ColumnDefinitions>
                <Ellipse Width="80" Height="80">
                    <Ellipse.Fill>
                        <RadialGradientBrush GradientOrigin="0.5,0.5">
                            <RadialGradientBrush.GradientStops>
                                <GradientStop Offset="0" Color="Red" />
                                <GradientStop Offset="0.6" Color="Green" />
                                <GradientStop Offset="1" Color="Blue" />
                            </RadialGradientBrush.GradientStops>
                        </RadialGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>
                <Button Grid.Column="2">
                    Button
                    <Button.Background>
                        Red
                    </Button.Background>
                </Button>
            </Grid>
        </Window.Content>
    </Window>
    View Code

    上面代码中Button有一个Content内容属性(可省略书写),其类型为Object,满足第一条;

    Background属性是Brush类型,它是一个抽象类,系统内置有其子类SolidColorBrush类型转化器,故将会把Red文本(对应的#FF0000这种RGB文本值也可以)转换为SolidColorBrush类型,满足第二条,所以可以写成上面那样。

    1.4自定义TypeConverter

    上面我们知道,WPF内置许多的TypeConverter,使得我们在使用Xaml时能够随心所欲。下面我们也得实现一个TypeConverter:

    首先创建一个Person类:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using XamlDemo;
    using System.ComponentModel;
    
    namespace XamlDemo
    {
        [TypeConverter(typeof(StringToPersonTypeConverter))]
        public class Person
        {
            public string Name { get; set; }
            public Person Child { get; set; }
        }
    }
    View Code

     然后创建一个继承自TypeConverter的StringToPersonTypeConverter,重写里面的ConvertFrom方法:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel;
    
    namespace XamlDemo
    {
        class StringToPersonTypeConverter:TypeConverter
        {
            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                if (value is string)
                {
                    Person p = new Person();
                    p.Name = value.ToString();
                    return p;
                }
                return base.ConvertFrom(context, culture, value);
            }
        }
    }
    View Code

     然后在Xaml中使用:

    <Window x:Class="XamlDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
            xmlns:local="clr-namespace:XamlDemo"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="20" />
            </Style>
            <XmlDataProvider x:Key="xdp1" XPath="Students/Student">
                <x:XData>
                    <Students xmlns="">
                        <Student Name="Jello"/>
                        <Student Name="Taffy"/>
                    </Students>
                </x:XData>
            </XmlDataProvider>
            <local:Person x:Key="person1" Child="Jello" />
        </Window.Resources>
        <Window.Content>
            <StackPanel x:Name="stackpanel1">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="100" />
                        <RowDefinition Height="5" />
                        <RowDefinition Height="100" />
                        <RowDefinition Height="5" />
                        <RowDefinition Height="100" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="5" />
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="5" />
                        <ColumnDefinition Width="100" />
                    </Grid.ColumnDefinitions>
                    <Ellipse Width="80" Height="80">
                        <Ellipse.Fill>
                            <RadialGradientBrush GradientOrigin="0.5,0.5">
                                <RadialGradientBrush.GradientStops>
                                    <GradientStop Offset="0" Color="Red" />
                                    <GradientStop Offset="0.6" Color="Green" />
                                    <GradientStop Offset="1" Color="Blue" />
                                </RadialGradientBrush.GradientStops>
                            </RadialGradientBrush>
                        </Ellipse.Fill>
                    </Ellipse>
                    <Button Grid.Column="2">
                        Button
                        <Button.Background>
                            #FF0000
                        </Button.Background>
                    </Button>
                    <TextBlock x:Name="txtblock1" Grid.Row="2" Text="Hello" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    <TextBlock Name="txtblock2" Grid.Row="2" Grid.Column="2" Text="Hello" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    <TextBlock Text="{Binding Path=Text,ElementName=myTextBlock1}" Background="{x:Static Member=SystemColors.GrayTextBrush}" Style="{x:Null}" Grid.Row="4" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    <ListBox Grid.Column="2" Grid.Row="4" ItemsSource="{Binding Source={StaticResource xdp1}}" DisplayMemberPath="@Name"/>
                </Grid>
                <local:MyTextBlock Text="Hello" x:Name="myTextBlock1"/>
                <local:MyTextBlock Text="Hello"/>
            </StackPanel>
        </Window.Content>
    </Window>
    View Code

     后台代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    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;
    
    namespace XamlDemo
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.txtblock1.Text = "World";
                this.txtblock2.Text = "World";
    
                //有x:Name修饰的MyTextBlock
                this.myTextBlock1.Text = "World";
                MessageBox.Show(this.myTextBlock1.Text);
    
                //无x:Name修饰的MyTextBlock
                MyTextBlock myTextBlock = this.stackpanel1.Children[2] as MyTextBlock;
                if (myTextBlock != null)
                {
                    myTextBlock.Text = "World";
                    MessageBox.Show(myTextBlock.Text);
                }
    
                //自定义TypeConverter
                Person p = this.FindResource("person1") as Person;
                if (p != null)
                {
                    MessageBox.Show(p.Child.Name);
                }
            }
        }
    }
    View Code

    这样,我们简单实现了一个TypeConverter。

    2.x命名空间

    由上面我们知道,xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"这个就是x命名空间,命名成x不是必须得,当然也可以命名成xmlns:y="http://schemas.microsoft.com/winfx/2006/xaml",这里只是约定而已(命名为x代表这是Xaml相关的命名空间)。我们来下里面都有些什么:

     

    有上图可以看出x命名空间中主要有Attribute、标记扩展和指令元素三种。下面我们将分别看下具体使用:

    2.1Attribute

    我们拿最常使用的x:Name来看下:

    Xaml代码:

    <Window x:Class="XamlDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Window.Content>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="100" />
                    <RowDefinition Height="5" />
                    <RowDefinition Height="100" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100" />
                    <ColumnDefinition Width="5" />
                    <ColumnDefinition Width="100" />
                </Grid.ColumnDefinitions>
                <Ellipse Width="80" Height="80">
                    <Ellipse.Fill>
                        <RadialGradientBrush GradientOrigin="0.5,0.5">
                            <RadialGradientBrush.GradientStops>
                                <GradientStop Offset="0" Color="Red" />
                                <GradientStop Offset="0.6" Color="Green" />
                                <GradientStop Offset="1" Color="Blue" />
                            </RadialGradientBrush.GradientStops>
                        </RadialGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>
                <Button Grid.Column="2">
                    Button
                    <Button.Background>
                        #FF0000
                    </Button.Background>
                </Button>
                <TextBlock x:Name="txtblock1" Grid.Row="2" Text="Hello" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                <TextBlock Name="txtblock2" Grid.Row="2" Grid.Column="2" Text="Hello" HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Grid>
        </Window.Content>
    </Window>
    View Code

     后台代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    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;
    
    namespace XamlDemo
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.txtblock1.Text = "World";
                this.txtblock2.Text = "World";
            }
        }
    }
    View Code

     运行发现两个TextBlock的文本显示都变成了World,那我们不经要问x:Name和Name有什么区别呢?我们知道TextBlock的Name属性是从FrameworkElement类中继承的,用于引用标签对应的实例,和ASPX中重的ID作用差不多,当一个标签或者自定义元素不是继承自FrameworkElement时,就无法获取它的Name属性了,这将导致在后台中访问该标签变得麻烦。我们举个例子来看下:

    首先我们自定义一个继承自UIElement的MyTextBlock类,只有一个Text属性。

    然后我们在Xaml中引用它,需要在根元素中加入xmlns:local="clr-namespace:XamlDemo",代码如下:

    Xaml代码:

    <Window x:Class="XamlDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
            xmlns:local="clr-namespace:XamlDemo"
            Title="MainWindow" Height="350" Width="525">
        <Window.Content>
            <StackPanel>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="100" />
                        <RowDefinition Height="5" />
                        <RowDefinition Height="100" />
                        <RowDefinition Height="5" />
                        <RowDefinition Height="100" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="5" />
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="5" />
                        <ColumnDefinition Width="100" />
                    </Grid.ColumnDefinitions>
                    <Ellipse Width="80" Height="80">
                        <Ellipse.Fill>
                            <RadialGradientBrush GradientOrigin="0.5,0.5">
                                <RadialGradientBrush.GradientStops>
                                    <GradientStop Offset="0" Color="Red" />
                                    <GradientStop Offset="0.6" Color="Green" />
                                    <GradientStop Offset="1" Color="Blue" />
                                </RadialGradientBrush.GradientStops>
                            </RadialGradientBrush>
                        </Ellipse.Fill>
                    </Ellipse>
                    <Button Grid.Column="2">
                        Button
                        <Button.Background>
                            #FF0000
                        </Button.Background>
                    </Button>
                    <TextBlock x:Name="txtblock1" Grid.Row="2" Text="Hello" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    <TextBlock Name="txtblock2" Grid.Row="2" Grid.Column="2" Text="Hello" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                </Grid>
                <local:MyTextBlock Text="Hello" x:Name="myTextBlock1"/>
                <local:MyTextBlock Text="Hello"/>
            </StackPanel>
        </Window.Content>
    </Window>
    View Code

    后台代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    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;
    
    namespace XamlDemo
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.txtblock1.Text = "World";
                this.txtblock2.Text = "World";
    
                //有x:Name修饰的MyTextBlock
                this.myTextBlock1.Text = "World";
                MessageBox.Show(this.myTextBlock1.Text);
    
                //无x:Name修饰的MyTextBlock
                MyTextBlock myTextBlock = this.stackpanel1.Children[2] as MyTextBlock;
                if (myTextBlock != null)
                {
                    myTextBlock.Text = "World";
                    MessageBox.Show(myTextBlock.Text);
                }
            }
        }
    }
    View Code

    其实,x:Name的作用有两个:一是为标签生成对应的实例并可以用x:Name的值引用它;二是如果这个标签继承自FrameworkElement类,那么也让其Name属性来引用生成的实例。

    2.2标记扩展

    Xaml标记扩展对应System.Window.Markup.MarkupExtension抽象类,查看它的子类,点击这里

    举个例子来说明它的用法:

    <Window x:Class="XamlDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
            xmlns:local="clr-namespace:XamlDemo"
            Title="MainWindow" Height="350" Width="525">
        <Window.Content>
            <StackPanel x:Name="stackpanel1">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="100" />
                        <RowDefinition Height="5" />
                        <RowDefinition Height="100" />
                        <RowDefinition Height="5" />
                        <RowDefinition Height="100" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="5" />
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="5" />
                        <ColumnDefinition Width="100" />
                    </Grid.ColumnDefinitions>
                    <Ellipse Width="80" Height="80">
                        <Ellipse.Fill>
                            <RadialGradientBrush GradientOrigin="0.5,0.5">
                                <RadialGradientBrush.GradientStops>
                                    <GradientStop Offset="0" Color="Red" />
                                    <GradientStop Offset="0.6" Color="Green" />
                                    <GradientStop Offset="1" Color="Blue" />
                                </RadialGradientBrush.GradientStops>
                            </RadialGradientBrush>
                        </Ellipse.Fill>
                    </Ellipse>
                    <Button Grid.Column="2">
                        Button
                        <Button.Background>
                            #FF0000
                        </Button.Background>
                    </Button>
                    <TextBlock x:Name="txtblock1" Grid.Row="2" Text="Hello" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    <TextBlock Name="txtblock2" Grid.Row="2" Grid.Column="2" Text="Hello" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    <TextBlock Text="{Binding Path=Text,ElementName=myTextBlock1}" Grid.Row="4" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                </Grid>
                <local:MyTextBlock Text="Hello" x:Name="myTextBlock1"/>
                <local:MyTextBlock Text="Hello"/>
            </StackPanel>
        </Window.Content>
    </Window>
    View Code

     上面代码只是多加了一个TextBlock,看起来奇怪的是它的Text、Background和Style属性的值,是用一对大括号来包括的,这就是我们要说的标记扩展。

    构造语法:{标记扩展类 [可选参数]},可选参数包括命名参数和定位参数。

    标记扩展类:x:Null、x:Static和Binding;

    命名参数:Binding中的Path和Element;

    定位参数:x:Static中Member

    这里还有一个{}转义序列的问题,请点击这里

    2.3指令元素

    主要就两个x:XData和x:Code,这里举个x:XData例子:

    <Window x:Class="XamlDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
            xmlns:local="clr-namespace:XamlDemo"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <Style TargetType="{x:Type TextBlock}">
                <Setter Property="FontSize" Value="20" />
            </Style>
            <XmlDataProvider x:Key="xdp1" XPath="Students/Student">
                <x:XData>
                    <Students xmlns="">
                        <Student Name="Jello"/>
                        <Student Name="Taffy"/>
                    </Students>
                </x:XData>
            </XmlDataProvider>
        </Window.Resources>
        <Window.Content>
            <StackPanel x:Name="stackpanel1">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="100" />
                        <RowDefinition Height="5" />
                        <RowDefinition Height="100" />
                        <RowDefinition Height="5" />
                        <RowDefinition Height="100" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="5" />
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="5" />
                        <ColumnDefinition Width="100" />
                    </Grid.ColumnDefinitions>
                    <Ellipse Width="80" Height="80">
                        <Ellipse.Fill>
                            <RadialGradientBrush GradientOrigin="0.5,0.5">
                                <RadialGradientBrush.GradientStops>
                                    <GradientStop Offset="0" Color="Red" />
                                    <GradientStop Offset="0.6" Color="Green" />
                                    <GradientStop Offset="1" Color="Blue" />
                                </RadialGradientBrush.GradientStops>
                            </RadialGradientBrush>
                        </Ellipse.Fill>
                    </Ellipse>
                    <Button Grid.Column="2">
                        Button
                        <Button.Background>
                            #FF0000
                        </Button.Background>
                    </Button>
                    <TextBlock x:Name="txtblock1" Grid.Row="2" Text="Hello" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    <TextBlock Name="txtblock2" Grid.Row="2" Grid.Column="2" Text="Hello" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    <TextBlock Text="{Binding Path=Text,ElementName=myTextBlock1}" Background="{x:Static Member=SystemColors.GrayTextBrush}" Style="{x:Null}" Grid.Row="4" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    <ListBox Grid.Column="2" Grid.Row="4" ItemsSource="{Binding Source={StaticResource xdp1}}" DisplayMemberPath="@Name"/>
                </Grid>
                <local:MyTextBlock Text="Hello" x:Name="myTextBlock1"/>
                <local:MyTextBlock Text="Hello"/>
            </StackPanel>
        </Window.Content>
    </Window>
    View Code

    3.总结

    这次主要是介绍Xaml相关的内容,讲的都是一些基础。自己在手写的过程,也重拾了已经遗忘的知识!

    4.参考文献

       1.MSDN(http://msdn.microsoft.com/zh-cn/library/ms754130%28v=vs.110%29.aspx)

       2.深入浅出WPF(刘铁猛)

       3.WPF高级编程

    5.Xaml编译(补充内容)

    Xaml的编译主要做三件事:

      1.转换为特殊的二进制格式Baml

      2.将Baml作为二进制资源嵌入程序集

      3.将Xaml和过程式代码链接起来

  • 相关阅读:
    DRF
    DRF
    DRF
    DRF
    RESTful介绍
    DRF parser请求处理流程
    Vue项目的创建
    怎么清除file控件的文件路径
    java用spring实现文件下载
    JS判断元素是否在数组内 阿星小栈
  • 原文地址:https://www.cnblogs.com/jellochen/p/3405119.html
Copyright © 2011-2022 走看看