zoukankan      html  css  js  c++  java
  • 建立一个使用.Net 2.0 MemberShip功能的标准例程(三)——绑定访问篇


    经过上一章的例子  我们已经建立了一个标准的,有很多有趣(甚至有些是专业级)功能的登陆系统了。
    可是我们如何管理这个系统呢?难道我们要用m$提供的asp.net管理工具管理一辈子么?

    ——当然不!那太可怕了!T_T

    ——我们要自己写一个后台,一个可以根据用户权限自己修改的后台!@_@

    当然有一种数据库狂人,他们只冷冷的瞥了几眼m$提供的数据库结构,轻描淡写的破译了其中所有的奥妙之处,随手拖了5-6个grid 写了10多行SQL 就用数据库方式搞定了。对于这种高手,我们仰慕,我们恨不得马上吸光他的百年功力,然后杀之后快,NND.

    但是成为这样的高手需要非常的经验和手腕。我们这些小菜鸟,没有写轮眼,也不是圣斗士,所谓“看穿”技能在我们的身上是不能工作的。我们只有membership标准对象  profile标准对象  和roles标准对象。

    难道就不能很方便的通过绑定方式访问这些对象么?





    通过页面访问较为复杂的对象——在.net 1.x 的时代——对我们曾是一种煎熬。明明好多对象有着数据行的特性,为什么不能直接访问呢?于是好多人---包括我,尝试过各种办法。我是失败那批5555,也有很多的人成功了,研究出一些很有效的办法。可是这个状况没有持续多久——自从.net 2.0推出了ODS ,我的失败阴影就再也不复回来~~~




    1     用ODS绑定MemberShip的总体思想

    如图所示
          


    从战略上 我们把每个用户看成一个行,把Membership中的GetAllUsers ()看成一个Select语句。
    但是一个标准的System.Web.Security.MembershipUser并不具有数据绑定对象的特性 
    比如主键/只读等信息所以我们可以重写它为它添加上这些特性

    2  简单的绑定方法
    这里采用的是MSDN上的部分c#代码修改成的VB代码  顺便我也把版权信息粘贴上来
    '/*
    '
    Copyright ?2005, Peter Kellner
    '
    All rights reserved.
    '
    http://peterkellner.net

    'Redistribution and use in source and binary forms, with or without
    '
    modification, are permitted provided that the following conditions
    '
    are met:

    '- Redistributions of source code must retain the above copyright
    '
    notice, this list of conditions and the following disclaimer.

    '- Neither Peter Kellner, nor the names of its
    '
    contributors may be used to endorse or promote products
    '
    derived from this software without specific prior written 
    '
    permission. 

    'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    '
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    '
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    '
    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
    '
    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    '
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
    '
    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
    '
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
    '
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
    '
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
    '
    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
    '
    POSSIBILITY OF SUCH DAMAGE.
    '
    */


    Imports System
    Imports System.Data
    Imports System.Configuration
    Imports System.Web
    Imports System.Web.Security
    Imports System.Web.UI
    Imports System.Web.UI.WebControls
    Imports System.Web.UI.WebControls.WebParts
    Imports System.Web.UI.HtmlControls
    Imports System.Collections.Generic
    Imports System.ComponentModel

     _



    '/ <summary>
    '
    / Summary description for MembershipUserWrapper
    '
    / This class is inherited from MembershipUser 
    '
    / Using the sytax public class ClassName (..) : base(initializers) allows for calling the
    '
    / contstructor of the base class.  In this case MembershipUser.
    '
    / </summary>
    '


    Namespace Membership_Tool


        
    Public Class MembershipUserWrapper
            
    Inherits MembershipUser


            
    '/ <summary>
            '/ This constructor is used to create a MembershipUserWrapper from a MembershipUser object.  MembershipUser is a default type used
            '/ in the Membership API provided with ASP.NET 2.0
            '/ </summary>
            '/ <param name="mu">MembershipUser object</param>
            Public Sub New(ByVal mu As MembershipUser)
                
    MyBase.New(mu.ProviderName, mu.UserName, mu.ProviderUserKey, mu.Email, mu.PasswordQuestion, mu.Comment, mu.IsApproved, mu.IsLockedOut, mu.CreationDate, mu.LastLoginDate, mu.LastActivityDate, mu.LastPasswordChangedDate, mu.LastLockoutDate)
            
    End Sub
     'New<DataObjectField(True)>  _



            
    '/ <summary>
            '/ This calls the base class UserName property.  It is here so we can tag
            '/ this property as the primary key so that datakeynames attribute gets set in the data control.
            '/ </summary>
            '
            <DataObjectField(True)> _
            
    Public Overrides ReadOnly Property UserName() As String
                
    Get
                    
    Return MyBase.UserName
                
    End Get
            
    End Property




            
    '/ <summary>
            '/ This constructor is used to create a MembershipUserWrapper from individual parameters values.  
            '/ For details of what each parameter means, see the Microsoft Membership class.
            '/ </summary>
            '/ <param name="comment">Passes to MembershipUser.comment</param>
            '/ <param name="creationDate">Passes to MembershipUser.creationDate</param>
            '/ <param name="email">Passes to MembershipUser.email</param>
            '/ <param name="isApproved">Passes to MembershipUser.isApproved</param>
            '/ <param name="lastActivityDate">Passes to MembershipUser.lastActivityDate</param>
            '/ <param name="lastLoginDate">Passes to MembershipUser.lastLoginDate</param>
            '/ <param name="passwordQuestion">Passes to MembershipUser.passwordQuestion</param>
            '/ <param name="providerUserKey">Passes to MembershipUser.providerUserKey</param>
            '/ <param name="userName">Passes to MembershipUser.userName</param>
            '/ <param name="lastLockoutDate">Passes to MembershipUser.lastLockoutDate</param>
            '/ <param name="providerName">Passes to MembershipUser.providerName</param>
            '
            Public Sub New(ByVal comment As StringByVal creationDate As DateTime, ByVal email As StringByVal isApproved As BooleanByVal lastActivityDate As DateTime, ByVal lastLoginDate As DateTime, ByVal passwordQuestion As StringByVal providerUserKey As ObjectByVal userName As StringByVal lastLockoutDate As DateTime, ByVal providerName As String)
                
    MyBase.New(providerName, userName, providerUserKey, email, passwordQuestion, comment, isApproved, False, creationDate, lastLoginDate, lastActivityDate, DateTime.Now, lastLockoutDate)
            
    End Sub
     'New
        End Class
     'MembershipUserWrapper
        ' This calls a constructor of MembershipUser automatically because of the base reference above

    End Namespace

    这样MembershipUser  的Username  被标记成 只读/主键 在绑定的时候  GridView会识别这一说明生成相应的编辑模式模板。






    数据行已经被我们建立好了,那么数据从哪里来呢?我们建立一个新类MembershipUserODS 把提供我们的数据的方法集中在这个类中,它就能起到Dataadepter的作用。
        <DataObject(True)> _
        
    Public Class MembershipUserODS

        
    End Class

    其中加入如下方法来实现数据的读取,也就是Select操作:
        <DataObjectMethod(DataObjectMethodType.Select, False)> Public Shared _
    Function GetMembers(ByVal returnAllApprovedUsers As BooleanByVal returnAllNotApprovedUsers As BooleanByVal usernameToFind As StringAs List(Of MembershipUserWrapper)




                
    Dim memberList As New List(Of MembershipUserWrapper)


                
    '看看是否只需要返回某个特定的用户
                If Not (usernameToFind Is NothingThen
                    
    '           {
                    Dim mu As MembershipUser = Membership.GetUser(usernameToFind)
                    
    If Not mu Is Nothing Then



                        
    Dim md As MembershipUserWrapper = New MembershipUserWrapper(mu)
                        memberList.Add(md)
                    
    End If

                
    Else

                    
    Dim muc As MembershipUserCollection = Membership.GetAllUsers()
                    
    Dim mu As MembershipUser
                    
    For Each mu In muc
                        
    If returnAllApprovedUsers = True And mu.IsApproved = True Or (returnAllNotApprovedUsers = True And mu.IsApproved = FalseThen
                            
    Dim md As New MembershipUserWrapper(mu)
                            memberList.Add(md)
                        
    End If
                    
    Next mu
           
    return  memberList
    end Function

    (在随后的源代码包里面有排序的功能 大家参考下就好)

    这时候我们已经可以对 MembershipUserODS   进行数据绑定了----这个方法实现了数据绑定的最基础的动作:选择。

    利用同样的方式  我们建立对应 插入、更新和删除操作的 方法:
    插入:
     <DataObjectMethod(DataObjectMethodType.Insert, True)> Public Shared _
        
    Sub Insert(ByVal userName As StringByVal isApproved As BooleanByVal comment As StringByVal lastLockoutDate As DateTime, ByVal creationDate As DateTime, ByVal email As StringByVal lastActivityDate As DateTime, ByVal providerName As StringByVal isLockedOut As BooleanByVal lastLoginDate As DateTime, ByVal isOnline As BooleanByVal passwordQuestion As StringByVal lastPasswordChangedDate As DateTime, ByVal password As StringByVal passwordAnswer As String)


                
    ' The incoming parameters, password and passwordAnswer are not properties of the
                ' MembershipUser class.  Membership has special member functions to deal with these
                ' two special properties for security reasons.  For this reason, they do not appear
                ' in a datacontrol that is created with this user object.  
                '
                ' the only reason you may want to have defaults is so you can build insert into your
                ' datacontrol.  A better approach would be to either follow the example shown in the
                ' Membership.asp page where the parameters are set directly to the userobject, or not
                ' include "new" at all in your control and use the other controls in the Membership API
                ' for creating new members.  (CreateUserWizard, etc)
                '
                ' It is recommended that you only enable the following lines if you are sure of what you are doing
                'if (password == null)
                '{
                '    password = "pass0word";
                '}
                'if (passwordAnswer == null)
                '{
                '    passwordAnswer = "Password Answer";
                '}


                
    Dim status As MembershipCreateStatus
                Membership.CreateUser(userName, password, email, passwordQuestion, passwordAnswer, isApproved, status)

                
    If status <> MembershipCreateStatus.Success Then
                    
    Throw New ApplicationException(status.ToString())
                
    End If

                
    Dim mu As MembershipUser = Membership.GetUser(userName)
                mu.Comment 
    = comment
                Membership.UpdateUser(mu)
            
    End Sub
     

    更新:
       <DataObjectMethod(DataObjectMethodType.Update, True)> Public Shared _
        
    Sub Update(ByVal UserName As StringByVal email As StringByVal isApproved As BooleanByVal comment As StringByVal lastActivityDate As DateTime, ByVal lastLoginDate As DateTime)
                
    Dim dirtyFlag As Boolean = False

                
    Dim mu As MembershipUser = Membership.GetUser(UserName)

                
    If mu.Comment Is Nothing Or (mu.Comment & "").CompareTo(comment) <> 0 Then
                    dirtyFlag 
    = True
                    mu.Comment 
    = comment
                
    End If

                
    If mu.Email Is Nothing Or (mu.Email & "").CompareTo(email) <> 0 Then
                    dirtyFlag 
    = True
                    mu.Email 
    = email
                
    End If

                
    If mu.IsApproved <> isApproved Then
                    dirtyFlag 
    = True
                    mu.IsApproved 
    = isApproved
                
    End If

                
    If dirtyFlag = True Then
                    Membership.UpdateUser(mu)
                
    End If
            
    End Sub
     

    删除:
         <DataObjectMethod(DataObjectMethodType.Delete, True)> Public Shared _
        
    Sub Delete(ByVal UserName As String)
                Membership.DeleteUser(UserName, 
    True)
            
    End Sub


    以上的工作,我们在ODS和membership之间  建立了一条桥梁,通过我们的代码,membership 复杂的对象被我们用简单的数据属性所代理,而能够被ODS正确识别

    下面我们简单的试验下我们工作的成果,亲手绑定一下。



    建立一个新页面Default.aspx  在上面放置一个GridView和一个ODS控件。
    效果如下图示:


    选择“配置数据源”,你刚才建立的、带有<DataObject>声明的类便会被枚举出来:


    下一步 选择每种动作的对应方法:



    当你把4种基本动作全部配置完毕  ,在属性栏把ObjectDataSource1.OldValuesParameterFormatString 的内容设置为{0}  就可以绑定了:




    基本上不会出现什么错误拉。。。。
     

    同样的方式   你可以用如下的代码建立Roles和Profile 的绑定


    Roles
    '/*
    '
    Copyright ?2005, Peter Kellner
    '
    All rights reserved.
    '
    http://peterkellner.net

    'Redistribution and use in source and binary forms, with or without
    '
    modification, are permitted provided that the following conditions
    '
    are met:

    '- Redistributions of source code must retain the above copyright
    '
    notice, this list of conditions and the following disclaimer.

    '- Neither Peter Kellner, nor the names of its
    '
    contributors may be used to endorse or promote products
    '
    derived from this software without specific prior written 
    '
    permission. 

    'THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    '
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    '
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    '
    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
    '
    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    '
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
    '
    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
    '
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
    '
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
    '
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
    '
    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
    '
    POSSIBILITY OF SUCH DAMAGE.
    '
    */


    Imports System
    Imports System.Data
    Imports System.Configuration
    Imports System.Web
    Imports System.Web.Security
    Imports System.Web.UI
    Imports System.Web.UI.WebControls
    Imports System.Web.UI.WebControls.WebParts
    Imports System.Web.UI.HtmlControls
    Imports System.Collections.Generic
    Imports System.ComponentModel
    Imports System.Collections.ObjectModel

    Namespace Membership_Tool



        
    '/ <summary>
        '/ A class used to encapsulate the Roles in ASP.NET Membermanagement 2.0
        '/ </summary>
        <DataObject(True)> _
        
    Public Class RoleDataODS
            
    ' This attribute allows the 

            
    '/ <summary>
            '/ Used to get all roles available
            '/ </summary>
            '/ <returns></returns>
            '
            <DataObjectMethod(DataObjectMethodType.Select, True)> Public Overloads Shared _
           
    Function GetRoles() As List(Of RoleData)
                
    Return GetRoles(NothingFalse)
            
    End Function
     'GetRoles


            
    '/ <summary>
            '/ Returns a collection of RoleData type values.  This specialized constructor lets you request by
            '/ an individual user
            '/ </summary>
            '/ <param name="userName">if null and showOnlyAssignedRolls==false, display all roles</param>
            '/ <param name="showOnlyAssignedRolls">if true, just show assigned roles</param>
            '/ <returns></returns>
            <DataObjectMethod(DataObjectMethodType.Select, False)> Public Overloads Shared _
           
    Function GetRoles(ByVal userName As StringByVal showOnlyAssignedRolls As BooleanAs List(Of RoleData)
                
    Dim roleList As New List(Of RoleData)

                
    Dim roleListStr As String() = Roles.GetAllRoles()
                
    Dim roleName As String
                
    For Each roleName In roleListStr
                    
    Dim userInRole As Boolean = False
                    
    ' First, figure out if user is in role (if there is a user)
                    If Not (userName Is NothingThen
                        userInRole 
    = Roles.IsUserInRole(userName, roleName)
                    
    End If

                    
    If showOnlyAssignedRolls = False Or userInRole = True Then
                        
    ' Getting usersInRole is only used for the count below
                        Dim usersInRole As String() = Roles.GetUsersInRole(roleName)
                        
    Dim rd As New RoleData()
                        rd.RoleName 
    = roleName
                        rd.UserName 
    = userName
                        rd.UserInRole 
    = userInRole
                        rd.NumberOfUsersInRole 
    = usersInRole.Length
                        roleList.Add(rd)
                    
    End If
                
    Next roleName

                
    ' FxCopy will give us a warning about returning a List rather than a Collection.
                ' We could copy the data, but not worth the trouble.
                Return roleList
            
    End Function
     'GetRoles


            
    '/ <summary>
            '/ Used for Inserting a new role.  Doesn't associate a user with a role.
            '/ This is not quite consistent with this object, but really what we want.
            '/ </summary>
            '/ <param name="RoleName">The Name of the role to insert</param>
            <DataObjectMethod(DataObjectMethodType.Insert, True)> Public Shared _
           
    Sub Insert(ByVal roleName As String)
                
    If Roles.RoleExists(roleName) = False Then
                    Roles.CreateRole(roleName)
                
    End If
            
    End Sub
     'Insert


            
    '/ <summary>
            '/ Delete any given role while first removing any roles associated with existing users
            '/ </summary>
            '/ <param name="roleName">name of role to delete</param>
            <DataObjectMethod(DataObjectMethodType.Delete, True)> Public Shared _
           
    Sub Delete(ByVal roleName As String)
                
    ' remove this role from all users.  not sure if deleterole does this automagically
                Dim muc As MembershipUserCollection = Membership.GetAllUsers()
                
    Dim allUserNames(1As String

                
    Dim mu As MembershipUser
                
    For Each mu In muc
                    
    If Roles.IsUserInRole(mu.UserName, roleName) = True Then
                        allUserNames(
    0= mu.UserName
                        Roles.RemoveUsersFromRole(allUserNames, roleName)
                    
    End If
                
    Next mu
                Roles.DeleteRole(roleName)
            
    End Sub
     'Delete
        End Class
     'RoleDataObject

        
    '/ <summary>
        '/ Dataobject class used as a base for the collection
        '/ </summary>
        Public Class RoleData

            
    ' Non normalized column which counts current number of users in a role
            Private number_OfUsersInRole As Integer

            
    Public Property NumberOfUsersInRole() As Integer
                
    Get
                    
    Return number_OfUsersInRole
                
    End Get
                
    Set(ByVal value As Integer)
                    number_OfUsersInRole 
    = value
                
    End Set
            
    End Property

            
    Private role_Name As String

            
    <DataObjectField(True)> _
            
    Public Property RoleName() As String
                
    Get
                    
    Return role_Name
                
    End Get
                
    Set(ByVal value As String)
                    role_Name 
    = value
                
    End Set
            
    End Property

            
    Public user_Name As String

            
    Public Property UserName() As String
                
    Get
                    
    Return user_Name
                
    End Get
                
    Set(ByVal value As String)
                    user_Name 
    = value
                
    End Set
            
    End Property

            
    Private user_InRole As Boolean

            
    Public Property UserInRole() As Boolean
                
    Get
                    
    Return user_InRole
                
    End Get
                
    Set(ByVal value As Boolean)
                    user_InRole 
    = value
                
    End Set
            
    End Property

        
    End Class
     'RoleData
    End Namespace

    Profile
    Imports System
    Imports System.Data
    Imports System.Configuration
    Imports System.Web
    Imports System.Web.Security
    Imports System.Web.UI
    Imports System.Web.UI.WebControls
    Imports System.Web.UI.WebControls.WebParts
    Imports System.Web.UI.HtmlControls
    Imports System.Collections.Generic
    Imports System.ComponentModel
    Namespace Membership_Tool
        
    <DataObject(True)> _
        
    Public Class ProfileODS
            
    <DataObjectMethod(DataObjectMethodType.Select, False)> _
            
    Public Function GetUserProfile(ByVal UserName As StringAs List(Of ProfileEntry)
                
    Return GetUserProfile(UserName, Nothing)


            
    End Function



            
    <DataObjectMethod(DataObjectMethodType.Select, False)> _
        
    Public Function GetUserProfile(ByVal UserName As StringByVal PropertyNames As StringAs List(Of ProfileEntry)

                
    Dim doFilter As Boolean = False
                
    Dim PNames As String() = Nothing
                
    If PropertyNames Is Nothing Then
                
    ElseIf PropertyNames = "" Then
                
    Else
                    PNames 
    = PropertyNames.Split("|")
                    doFilter 
    = True
                
    End If

                
    Dim pelist As New List(Of ProfileEntry)
                
    Dim pf As Profile.ProfileBase = System.Web.Profile.ProfileBase.Create(UserName)
                
    '因为PrifileCommon的一个未知bug 在没有访问任何已知属性前,属性的集合将不可访问,所以需要一个缺省的属性来支持
                '以下为试图访问默认的属性bugjumper来获得属性集合
                Try
                    
    Dim x As String = pf.GetPropertyValue("bugjumper")
                
    Catch ex As Exception
                    
    '  Throw New Exception("管理员并没有为profilecommon的bug设置默认的属性参数bugjumper")
                End Try
                
    '处理bug过程结束


                
    For Each itm As System.Configuration.SettingsPropertyValue In pf.PropertyValues

                    
    If itm.Name <> "bugjumper" Then '忽略处理bug用的公共属性
                        Dim doAdd As Boolean = False
                        
    If Not doFilter Then
                            doAdd 
    = True
                        
    ElseIf Array.IndexOf(PNames, itm.Name) <> -1 Then
                            doAdd 
    = True

                        
    End If
                        
    If doAdd Then
                            
    Dim pe As New ProfileEntry(UserName, itm.Name, itm.PropertyValue)
                            pelist.Add(pe)
                        
    End If
                   
                    
    End If


                
    Next




                
    Return pelist

            
    End Function

            
    <DataObjectMethod(DataObjectMethodType.Update, False)> _
            
    Public Sub UpdateUserProfile(ByVal UserName As StringByVal Key As StringByVal Value As String)
                
    Dim pf As Profile.ProfileBase = System.Web.Profile.ProfileBase.Create(UserName)
                pf.SetPropertyValue(Key, Value)
                pf.Save()
            
    End Sub

        
    End Class








        
    Public Class ProfileEntry
            
    Private k, v As String
            
    Private U As String
            
    <DataObjectField(True)> _
       
    Property Key() As String
                
    Get
                    
    Return k
                
    End Get
                
    Set(ByVal value As String)
                    k 
    = value
                
    End Set
            
    End Property



            
    Property Value() As String
                
    Get
                    
    Return v
                
    End Get
                
    Set(ByVal value As String)
                    v 
    = value
                
    End Set
            
    End Property


            
    <DataObjectField(True)> _
            
    Property UserName() As String
                
    Get
                    
    Return U
                
    End Get
                
    Set(ByVal value As String)
                    U 
    = value
                
    End Set
            
    End Property

            
    Sub New(ByVal NewUserName As StringByVal NewKey As StringByVal NewValue As String)
                
    Me.UserName = NewUserName
                
    Me.Key = NewKey
                
    Me.Value = NewValue
            
    End Sub

        
    End Class



    End Namespace

    ProFile可是我自己写的阿^_^

    这里发现了一个bug,由vs2005 生成的profilecommon类 在第一次成功访问某属性前,Properties集合不会正确的枚举出所有成员,所以我在程序中作了一些小小的调整,具体请看示例工程:DDDD
     
    下载地址  C#核心代码
    -------------------------------
    终于填完了  连续睡眠不足词不达意  回头再作修改555555
  • 相关阅读:
    网页中加入Flash的代码
    AJAX示例
    图片过滤效果
    ASP.NET性能优化
    什么是AJAX
    动态获取enum的值并且添到List中
    VSTO为Excel快捷菜单添加项
    OpenXML for office SDK 2.5 Download url.
    WebClient
    C#监控本地目录文件变化
  • 原文地址:https://www.cnblogs.com/waynebaby/p/591882.html
Copyright © 2011-2022 走看看