开发和使用自定义服务器控件

 

·                      创建一个ASP.NET服务器控件。

·                      向该控件及其成员添加元数据以控制安全性和设计时行为。

·                      使用 ASP.NET网站中的 App_Code目录对控件进行测试(无需手动编译步骤)。

·                      在配置文件和该控件的程序集中指定一个标记前缀。

·                      将该控件编译为一个程序集并将其添加到Bin目录中。

·                      将一个位图嵌入到该控件的程序集中,以作为可视化设计器的工具箱图标使用。

·                      在页中使用已编译的控件。

可视化设计工具(如Microsoft Visual Studio 2005)可以简化控件的开发过程,但并不是创建或生成自定义控件的必不可少的工具。可以通过使用任何文件编辑器创建控件,并使用.NET Framework SDK附带的编译器从命令行生成这些控件。无论以何种方式创建控件,在可视化设计器中控件的设计时外观和行为都将相同。页开发人员可将控件添加到可视化设计器的工具箱,可将其拖动到设计图面上,还可以在属性浏览器中访问其属性和事件。在有些可视化设计器(如Visual Studio 2005)中,自定义控件还可自行支持 IntelliSense

创建服务器控件

要创建的控件WelcomeLabel是一个简单控件,与标准的Label控件类似。WelcomeLabel类从 WebControl派生,它定义了一个Text属性,该属性允许页开发人员提供一个文本字符串,以欢迎访问站点的用户。如果用户名出现在用户浏览器发送的标题中,WelcomeLabel就会将用户名追加到该文本字符串。有关检索用户名的更多信息,请参见User。例如,如果页开发人员将Hello”设置为Text属性的值,则根据标题中是否出现用户名WelcomeLabel将呈现Hello, userName!“Hello!”

创建自定义服务器控件的代码

1.         创建一个名为 WelcomeLabel.cs WelcomeLabel.vb的文件。

2.         将以下代码添加到控件的源文件中:

Visual Basic

' WelcomeLabel.vb Option Strict On Imports System Imports System.ComponentModel Imports System.Security.Permissions Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Namespace Samples.AspNet.VB.Controls < _ AspNetHostingPermission(SecurityAction.Demand, _ Level:=AspNetHostingPermissionLevel.Minimal), _ AspNetHostingPermission(SecurityAction.InheritanceDemand, _ Level:=AspNetHostingPermissionLevel.Minimal), _ DefaultProperty("Text"), _ ToolboxData( _ "<{0}:WelcomeLabel runat=""server""> </{0}:WelcomeLabel>") _ > _ Public Class WelcomeLabel Inherits WebControl < _ Bindable(True), _ Category("Appearance"), _ DefaultValue(""), _ Description("The welcome message text."), _ Localizable(True) _ > _ Public Overridable Property Text() As String Get Dim s As String = CStr(ViewState("Text")) If s Is Nothing Then s = String.Empty Return s End Get Set(ByVal value As String) ViewState("Text") = value End Set End Property Protected Overrides Sub RenderContents( _ ByVal writer As HtmlTextWriter) writer.WriteEncodedText(Text) If Context IsNot Nothing Then Dim s As String = Context.User.Identity.Name If (s IsNot Nothing) AndAlso (s <> String.Empty) Then Dim split() As String = s.Split("/".ToCharArray) Dim n As Integer = split.Length - 1 If (split(n) <> String.Empty) Then writer.Write(", ") writer.Write(split(n)) End If End If End If writer.Write("!") End Sub End Class End Namespace

C#

复制代码

// WelcomeLabel.cs using System; using System.ComponentModel; using System.Security.Permissions; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace Samples.AspNet.CS.Controls { [ AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal), DefaultProperty("Text"), ToolboxData("<{0}:WelcomeLabel runat=/"server/"> </{0}:WelcomeLabel>") ] public class WelcomeLabel : WebControl { [ Bindable(true), Category("Appearance"), DefaultValue(""), Description("The welcome message text."), Localizable(true) ] public virtual string Text { get { string s = (string)ViewState["Text"]; return (s == null) ? String.Empty : s; } set { ViewState["Text"] = value; } } protected override void RenderContents(HtmlTextWriter writer) { writer.WriteEncodedText(Text); if (Context != null) { string s = Context.User.Identity.Name; if (s != null && s != String.Empty) { string[] split = s.Split('//'); int n = split.Length - 1; if (split[n] != String.Empty) { writer.Write(", "); writer.Write(split[n]); } } } writer.Write("!"); } } }

代码讨论

下面的代码讨论并不是执行此演练中的步骤必不可少的内容,可以在开始时跳过。但是,如果您初学控件创作,我们建议您至少要在完成演练后阅读此内容。

如果控件要呈现用户界面 (UI)元素或任何其他客户端可见的元素,则应从System.Web.UI.WebControls.WebControl或派生类)派生该控件。如果控件要呈现在客户端浏览器中不可见的元素(如隐藏元素或meta元素),则应从 System.Web.UI.Control派生该控件。WebControl类从 Control派生,并添加了与样式相关的属性,如FontForeColor BackColor。此外,一个从 WebControl派生的控件也自行参与到 ASP.NET的主题功能。

如果您的控件要扩展现有控件(如ButtonLabel Image控件)的功能,则可以从该控件派生。由于WelcomeLabel扩展了 Label控件的功能,因此它可从 Label派生。但是,本演练从WebControl派生WelcomeLabel以演示如何定义属性和定义属性元数据。

WelcomeLabel定义一个Text属性,并使用视图状态存储该属性值。使用视图状态保存回发间的Text值。每次回发时,将重新创建页并从视图状态还原值。如果Text值并未存储在视图状态中,则在每次回发时会将值设置为其默认的EmptyViewState属性继承自WebControl,是保存数据值的字典。通过使用String键,可输入和检索值。本例中将Text用作键。字典中的项被类型化为 Object,然后必须将其强制转换为属性类型。有关更多信息,请参见ASP.NET态管理概述。

WelcomeLabel控件通过重写继承的RenderContents方法呈现其Text属性。传入RenderContents方法的参数是 HtmlTextWriter类型的对象,是具有呈现标记和其他 HTML(和HTML变量)标记的方法的实用工具类。

注意,WelcomeLabel会连续调用HtmlTextWriter对象的Write方法,而不是先执行字符串串联然后调用 Write方法。由于HtmlTextWriter对象直接写入输出流,因此这样可以提高性能。字符串串联需要时间和内存来创建字符串,然后写入流。在控件中实现呈现时,应按照本演练中说明的模式进行操作。

通常,在从 WebControl派生控件并呈现单个元素时,应重写RenderContents方法(而不是Render方法),以呈现控件标记中的内容。在呈现控件及其样式属性的开始标记之后,WebControl Render方法将调用RenderContents如果重写Render方法以写入内容,则控件将丢失生成到 WebControl Render方法中的样式呈现逻辑。有关呈现从WebControl派生的控件的更多信息,请参见 Web控件呈现示例

应用于WelcomeLabel的属性包含由公共语言运行库和设计时工具使用的元数据。

在类级别上,通过以下属性标记WelcomeLabel

·                      AspNetHostingPermissionAttribute是代码访问安全属性。该属性使 JIT编译器检查链接到WelcomeLabel的代码是否具有AspNetHostingPermission权限。所有的公共 ASP.NET均使用此属性标记。应将AspNetHostingPermissionAttribute应用于控件,以对部分受信任的被调用方进行安全检查。

·                      DefaultPropertyAttribute是设计时属性(Attribute),它指定控件的默认属性(Property)在可视化设计器中,当页开发人员在设计图面上单击控件时,属性浏览器通常突出显示此默认属性。

·                      ToolboxDataAttribute指定元素的格式字符串。如果在工具箱中双击控件或将其从工具箱拖动到设计图面上时,该字符串将成为控件的标记。对于WelcomeLabel该字符串创建此元素:

<aspSample:WelcomeLabel runat="server"> </aspSample:WelcomeLabel>

WelcomeLabel控件还从WebControl基类继承了两个属性ParseChildrenAttribute PersistChildrenAttribute它们被应用为ParseChildren(true)PersistChildren(false)这两个属性(Attribute)一起与 ToolboxDataAttribute属性 (Attribute)共同使用,这样可将子元素解释为属性(Property),并将属性 (Property)作为属性 (Attribute)留。

以下应用于WelcomeLabelText属性 (Property)的属性 (Attribute)是标准设计时属性(Attribute),通常会将标准设计时属性 (Attribute)应用于控件的所有公共属性 (Property)

·                      BindableAttribute(被指定为 true false),指定将属性绑定到数据对可视化设计器是否有意义。例如,在 Visual Studio 2005中,如果属性标记为Bindable(true),则该属性可显示在数据绑定对话框中。如果属性 (Property)没有使用此属性 (Attribute)标记,则属性 (Property)浏览器会推断其值为Bindable(false)

·                      CategoryAttribute指定如何在可视化设计器的属性浏览器中对属性进行分类。例如,当页开发人员使用属性浏览器的分类视图时,Category("Appearance")将告知属性浏览器在外观类别中显示属性。可以根据属性浏览器中的现有类别指定字符串参数,也可以创建自己的类别。

·                      DescriptionAttribute指定属性的简短描述。在 Visual Studio 2005中,属性浏览器将在属性窗口底部显示选定的属性的描述。

·                      DefaultValueAttribute指定属性的默认值。此值应与从属性访问器 (getter)返回的默认值相同。在 Visual Studio 2005中,DefaultValueAttribute允许页开发人员通过在属性窗口中唤出快捷菜单然后单击重置按钮将属性值重置为其默认值。

·                      LocalizableAttribute(指定为 true false)指定本地化属性对可视化设计器是否有意义。当某属性标记为Localizable(true)时,可视化设计器会在对本地化资源进行序列化时包含该属性。对控件轮询可本地化的属性时,设计器会将此属性值保存到非特定于区域性的资源文件或其他本地化源中。

应用于控件及其成员的设计时属性在运行时不会影响控件的功能,但在可视化设计器中使用控件时,这些属性能提升开发人员的体验。有关服务器控件的设计时、分析时和运行时属性的完整列表,可参见自定义服务器控件的元数据属性。

使用 App_Code目录测试控件(无需编译)

可以使用 ASP.NET动态编译功能对页中的控件进行测试,而无需将控件编译为程序集。ASP.NET能动态编译 ASP.NET网站根目录下 App_Code目录中放置的代码。这样就可以从页访问 App_Code中源文件中的类,而无需将其手动编译为程序集。如果将控件的源文件放入了 App_Code目录,则对控件的代码所做的更改能立即在使用这些控件的页中反映出来。

创建 ASP.NET网站和 App_Code目录

1.         创建一个名为ServerControlsTest的网站。可以在 IIS中将该站点创建为名为ServerControlsTest的虚拟目录。有关创建和配置 IIS虚拟目录的详细信息,请参见如何:在 IIS中创建和配置虚拟目录。

2.         直接在网站根目录(也称 Web应用程序根目录)下创建一个 App_Code目录。

3.         将控件的源文件(WelcomeLabel.cs WelcomeLabel.vb)复制到 App_Code目录。

创建标记前缀

标记前缀是指在页中以声明方式创建控件时出现在控件类型名称前面的前缀,如 <asp:Table />中的“asp”。若要在页中以声明方式使用您的控件,则 ASP.NET需要一个映射到该控件的命名空间的标记前缀。通过在每个使用自定义控件的页上添加一个 @ Register指令,页开发人员可提供标记前缀/命名空间映射,如下面的示例所示:

C#

<%@ Register TagPrefix="aspSample" Namespace="Samples.AspNet.CS.Controls"%>

[Visual Basic]

<%@ Register TagPrefix="aspSample" Namespace="Samples.AspNet.VB.Controls"%>

除了在每个 .aspx页中使用 @ Register指令,页开发人员还可以在 Web.config文件中指定标记前缀/命名空间映射。如果将在 Web应用程序的多个页中使用自定义控件,则该方法非常有用。下面的过程描述如何在 Web.config文件中指定标记前缀映射。

Web.config文件中添加标记前缀映射

1.         如果不存在名为 Web.config的文本文件,则请在网站的根目录下创建该文件。

2.         创建了新的(空的)Web.config文件之后,将以下代码复制到该文件中并保存该文件。

C#

<?xml version="1.0"?> <configuration> <system.web> <pages> <controls> <add tagPrefix="aspSample" namespace="Samples.AspNet.CS.Controls"> </add> </controls> </pages> </system.web> </configuration>

Visual Basic

<?xml version="1.0"?> <configuration> <system.web> <pages> <controls> <add tagPrefix="aspSample" namespace="Samples.AspNet.VB.Controls"> </add> </controls> </pages> </system.web> </configuration>

突出显示部分是一个标记前缀项,该项将标记前缀“aspSample”映射到命名空间Samples.AspNet.CS.ControlsSamples.AspNet.VB.Controls

3.         如果已存在一个 Web.config文件,则请将前一步骤中突出显示的文本作为该配置文件的 controls元素的子项添加到该文件中。如果 Web.config文件中没有 controls pages元素,则请按照前一步骤中介绍的方法创建这些元素。

在配置文件中指定了标记前缀映射之后,可以在网站的任何页中以声明方式使用WelcomeLabel控件(如<aspSample:WelcomeLabel />)。

创建使用控件的页

创建使用自定义控件的页

1.         在网站中创建一个名为 WelcomeLabelTest.aspx的文本文件。

2.         将以下标记复制到 WelcomeLabelTest.aspx文件中并保存该文件。

Visual Basic

<%@ Page Language="VB"%> <html> <head id="Head1" runat="server"> <title>WelcomeLabel Test</title> </head> <body> <form id="form1" runat="server"> <div> <aspSample:WelcomeLabel Text="Hello" ID="WelcomeLabel1" runat="server" BackColor="Wheat" ForeColor="SaddleBrown" /> </div> </form> </body> </html>

C#

<%@ Page Language="C#"%> <html> <head id="Head1" runat="server"> <title>WelcomeLabel Test</title> </head> <body> <form id="form1" runat="server"> <div> <aspSample:WelcomeLabel Text="Hello" ID="WelcomeLabel1" runat="server" BackColor="Wheat" ForeColor="SaddleBrown" /> </div> </form> </body> </html>

3.         在地址栏中输入以下 URL,以在浏览器中显示 WelcomeLabelTest.aspx

http://localhost/ServerControlsTest/WelcomeLabelTest.aspx

4.         对控件的源代码做一些更改。例如,通过在 RenderContents方法末尾处添加此行代码来多写一个字符串:

C#

writer.Write("Testing how the App_Code directory works.");

Visual Basic

writer.Write("Testing how the App_Code directory works.")

5.         在浏览器中刷新 WelcomeLabelTest.aspx页。

将看到,尽管没有编译控件,对控件所做的更改仍在页中反映出来了。

除了显式定义的WelcomeLabel控件的Text属性之外,从页中的控件实例可以看到,它还具有之前并没有定义的 BackColor ForeColor属性。WelcomeLabel控件通过从 WebControl基类继承,从而获取与样式相关的这些属性和其他属性。此外,WelcomeLabel可自行分配外观并成为主题的一部分。

将控件编译为程序集

尽管利用 App_Code目录可以不编译就对控件进行测试,但是如果您希望将您的控件作为对象代码分发给其他开发人员,则必须对其进行编译。此外,如果没有将控件编译为程序集,就不能将该控件添加到可视化设计器的工具箱中。

将控件编译为程序集

1.         按照以下这些步骤设置计算机的 Windows环境 PATH变量,使其包含 .NET Framework的安装路径:

a.              Windows中,右击我的电脑,选择属性,单击高级选项卡,然后单击环境变量按钮。

b.             系统变量列表中,双击 Path变量。

c.              变量值文本框中,将一个分号 (;)添加到文本框中的现有值的末尾,然后键入您的 .NET Framework安装路径。.NET Framework通常安装在位于 /Microsoft.NET/Framework/versionNumber Windows安装目录中。

d.             单击确定关闭每个对话框。

2.         从为本演练第一步中的源文件创建的目录中运行以下命令。

3.         C#

4.                csc /t:library /out:Samples.AspNet.CS.Controls.dll /r:System.dll /r:System.Web.dll *.cs

5.         Visual Basic

6.                vbc /t:library /out:Samples.AspNet.VB.Controls.dll /r:System.dll /r:System.Web.dll *.vb

7.         /t:library编译器选项告知编译器创建一个库,而不是创建一个可执行程序集。/out选项为程序集提供名称,而 /r选项则列出链接到您的程序集的那些程序集。

为保持示例独立,本演练要求创建具有单个控件的程序集。通常,.NET Framework设计指导原则建议不要创建只包含很少的类的程序集。为了便于部署,应尽可能少地创建程序集。

在控件程序集中嵌入图标

可视化设计器(如Visual Studio 2005)通常使用默认图标(如齿轮图像)显示工具箱中的控件。作为控件的一个选项,可以通过在控件的程序集中嵌入一个 16*16像素的位图来自定义控件在工具箱中的外观。根据约定,可视化设计器将该位图最下方左侧的像素用作透明色。

在控件程序集中嵌入图标

1.         创建或获取一个 16*16像素的位图,将其作为控件的工具箱图标。

2.         将该位图命名为 WelcomeLabel.bmp

3.         将该位图文件添加到包含WelcomeLabel控件源文件的目录(CustomControlsCS CustomControlsVB)中。

4.         从包含源文件的目录中运行以下命令:

C#

csc /res:WelcomeLabel.bmp,Samples.AspNet.CS.Controls.WelcomeLabel.bmp /t:library /out:Samples.AspNet.CS.Controls.dll /r:System.dll /r:System.Web.dll *.cs

Visual Basic

vbc /res:WelcomeLabel.bmp,Samples.AspNet.VB.Controls.WelcomeLabel.bmp /t:library /out:Samples.AspNet.VB.Controls.dll /r:System.dll /r:System.Web.dll *.vb

此命令将编译控件并将该位图作为资源嵌入到程序集中。嵌入的位图资源的名称必须与关联控件的命名空间限定名称完全相同。例如,如果控件名称为Samples.AspNet.CS.Controls.WelcomeLabel,则嵌入的位图的名称必须为Samples.AspNet.CS.Controls.WelcomeLabel.bmp。此命名约定使可视化设计器能自动将该位图用作控件的工具箱图标。如果不使用此命名约定,则必须将 ToolboxBitmapAttribute应用于该控件,以指定嵌入的位图资源的名称。

使用 TagPrefixAttribute提供标记前缀/命名空间映射

在本演练前面的内容中,在介绍使用 App_Code目录时讲述了页开发人员如何在页或 Web.config文件中指定标记前缀。可以选择通过包含程序集级别的 System.Web.UI.TagPrefixAttribute属性,建议可视化设计器应为控件使用的默认标记前缀。如果设计器未找到在 Web.config文件或页中的 Register指令中映射的标记前缀,TagPrefixAttribute属性就会为可视化设计器提供可以使用的标记前缀,因此该属性非常有用。在工具箱中首次双击控件或将其从工具箱拖动到页中时,此标记前缀将向页进行注册。

如果决定使用 TagPrefixAttribute属性,则可以在与控件一起编译的单独文件中指定该属性。根据约定,该文件名为 AssemblyInfo.languageExtension,如 AssemblyInfo.cs AssembyInfo.vb。下面的过程介绍如何指定 TagPrefixAttribute元数据。

使用 TagPrefixAttribute添加命名空间/标记前缀映射

1.         在源代码目录中创建一个名为 AssemblyInfo.cs AssemblyInfo.vb的文件并向该文件添加以下代码。

C#

using System; using System.Web.UI; [assembly: TagPrefix("Samples.AspNet.CS.Controls", "aspSample")]

Visual Basic

Imports System Imports System.Web.UI <Assembly: TagPrefix("Samples.AspNet.VB.Controls", "aspSample")>

标记前缀属性在命名空间Samples.AspNet.CS.ControlsSamples.AspNet.VB.Controls与前缀aspSample之间创建映射。

2.         使用前面使用的编译命令重新编译所有的源文件(使用或不使用嵌入资源)。

ASP.NET页中使用已编译的自定义控件

若要测试自定义控件的已编译版本,则必须使网站中的页可以访问此控件的程序集。

使网站可以访问控件的程序集

1.         在网站的根目录下创建一个 Bin目录。

2.         将控件程序集(Samples.AspNet.CS.Controls.dll Samples.AspNet.VB.Controls.dll)复制到 Bin目录。

3.          App_Code目录中删除该控件的源文件。

如果没有删除源文件,则您的控件的类型将同时存在于编译的程序集中和由 ASP.NET动态生成的程序集中。这在加载该控件时会造成不明确引用,加载任何使用该控件的页时将生成编译器错误。

本演练中创建的程序集必须放在 ASP.NET网站的 Bin目录中才能使网站中的页使用您的控件,故而该程序集为私有程序集。如果其他应用程序没有安装该程序集的副本,则这些应用程序都不能访问该程序集。如果要为共享的 Web宿主应用程序创建控件,通常会将控件打包到一个私有程序集中。但是,如果要创建在专用宿主环境中使用的控件,或要创建 ISP向所有客户提供的一系列控件,则需要将您的控件打包到安装在全局程序集缓存中的一个共享(强名称)程序集中。有关更多信息,请参见使用程序集和全局程序集缓存。

接下来,必须修改在 Web.config中创建的标记前缀映射,以指定控件的程序集名称。

修改 Web.config中的标记前缀映射

·                      编辑 Web.config文件,向 addtagPrefix元素添加 assembly属性:

C#

<controls> <add tagPrefix="aspSample" namespace="Samples.AspNet.CS.Controls" assembly="Samples.AspNet.CS.Controls"> </add> </controls>

Visual Basic

<controls> <add tagPrefix="aspSample" namespace="Samples.AspNet.VB.Controls" assembly="Samples.AspNet.VB.Controls"> </add> </controls>

assembly属性指定控件所在的程序集的名称。addtagPrefix元素将标记前缀映射到命名空间和程序集的组合。ASP.NET App_Code目录中的源文件动态生成程序集时,无需提供程序集属性。未使用程序集属性时,ASP.NET会从通过 App_Code目录动态生成的程序集加载控件的类型。

查看使用自定义控件的页

·                      通过在地址栏中输入以下 URL,可在浏览器中显示 WelcomeLabelTest.aspx页:

http://localhost/ServerControlsTest/WelcomeLabelTest.aspx

如果在可视化设计器(如 Visual Studio 2005)中使用您的控件,可以向工具箱添加您的控件,可以将其从工具箱拖动到设计图面,还可以在属性浏览器中访问其属性和事件。此外,在 Visual Studio 2005中,您的控件在页设计器的视图和代码编辑器中具有完整的 IntelliSense支持。这包括 script块中的语句结束支持以及页开发人员单击控件标记时的属性浏览器支持。

后续步骤

本演练演示了如何开发简单的自定义 ASP.NET服务器控件以及如何在页中使用该控件。您了解了如何定义属性以及如何将控件编译为程序集。有关更多信息,包括有关呈现、定义属性、维护状态和实现复合控件的信息,请参见开发自定义 ASP.NET服务器控件。

在本演练中,您了解了可以为您的控件提供自定义工具箱图标。您还学习了如何添加设计时元数据,从而为您的控件自定义属性浏览器支持。通过使用在设计时和运行时提供不同用户界面的可视化设计器类,复杂控件(如 GridView控件)进一步增强了其设计时体验。ASP.NET 2.0为服务器控件提供的设计器对象模型与 ASP.NET 1.0 1.1中提供的模型大为不同。若要了解有关在 ASP.NET 2.0中为您的控件实现自定义设计器类的信息,请参见 ASP.NET控件设计器概述。

ASP.NET 2.0中,服务器控件通过使用适配器类可以为客户端浏览器或设备定义不同行为。有关更多信息,请参见开发 ASP.NET服务器控件的适配器

不同的浏览器和相同浏览器的不同版本支持不同的功能。ASP.NET服务器控件将自动确定已请求 .aspx页的浏览器,并为该浏览器正确设置所生成的 HTML标记的格式。但是,部分控件功能无法在较低版本的浏览器上呈现,因此需要在尽可能多的浏览器类型上查看页的输出,以确保页以预期方式呈现在所有浏览器中。有关更多信息,请参见 ASP.NET Web服务器控件和浏览器功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值