ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 Excel Home精品图文教程库
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
300集Office 2010微视频教程 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
楼主: fanjy

[分享]《RibbonX:自定义Office 2007功能区》阅读笔记

  [复制链接]

TA的精华主题

TA的得分主题

 楼主| 发表于 2008-4-19 10:44 | 显示全部楼层
3、处理事件
Excel、Access和Word都能控制许多不同类型的事件。事件通常与常使用的对象相关联。
(1)工作簿事件
例如,工作簿的SheetBeforeRightClick事件:
Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, _
ByVal Targe As Range,Cancel As Boolean)
If Union(Target.Range(“A1”),Range(“A1:C25”)).Address= _
    Range(“A1:C25”).Address Then Cancel=True
End Sub
其中,参数sh引用发生右击的工作表,参数Target引用右击的目标区域,参数Cancel决定是否取消右击的结果。正常情况下,右击时将获得一个弹出菜单,但当Cancel设置为True后,将取消该弹出菜单。
上述代码对工作簿中所有工作表都有效。
一些有用的工作簿事件:BeforeClose、BeforeSave、NewSheet、Open、SheetActivate、SheetBeforeRightClick、SheetChange。
(2)工作表事件
当必须处理某工作表上的事件时,使用该工作表自身的事件。下面举一个实用的解决方案。
假设需要验证A列接受不重复的值,只要用户在每个单元格中输入数据而不在单元格区域中复制和粘贴,那么一切都正常。关键是,如果用户复制并粘贴数据,那么所有的验证都是徒劳。此时,应该使用事件过程。
使用Change事件来检查在列中发生了什么,如果用户粘贴数据,则该过程验证粘贴值以便确定撤销在该列中的粘贴操作。如下面的代码:
Private Sub Worksheet_Change(ByVal Target As Range)
    Dim rngSelected        As Range
    Dim rngValue           As Range
    Dim lngCount           As Long
    '当检查发生时禁用事件
    Application.EnableEvents = False
    '监控的单元格区域
    If Union(Target.Range("A1"), Range("A1:A1048576")).Address = _
        Range("A1:A1048576").Address Then
        On Error Resume Next
        '设置所选择的区域
        Set rngSelected = Selection
        '统计所有粘贴的值的重复数
        For Each rngValue In rngSelected
            lngCount = WorksheetFunction.CountIf(Range("A1:A1048576"), _
                rngValue)
            '如果重复数大于1
            If lngCount > 1 Then
                '显示错误消息
                MsgBox "列A仅接受唯一值." _
                    & "这些值您已经输入.", vbCritical, _
                    "在列A复制的值"
                '撤销粘贴操作
                Application.Undo
               
                Application.CutCopyMode = xlCut
                Exit For
            End If
        Next rngValue
    End If
    '启用事件以使继续监控
    Application.EnableEvents = True
End Sub
注意,因为代码也导致工作表中的变化,所以必须临时禁用事件。此外,在过程结束前,必须启用事件。
一些有用的工作表事件:Activate、BeforeDoubleClick、BeforeRightClick、Change、SelectionChange。
(3)在Access中的窗体和报表事件
假设用户打开某报表,但什么也没看到,即在报表中没有放置数据。空的报表将给人在过程中出现错误的印象,而不是报表中没有数据。此时,使用NoData事件来确定是否取消报表,来避免这些问题和混淆
Private Sub Report_NoData(cancel As Integer)
MsgBox “目前没有可用的数据。报表将被删除…”
cancel=-1 ‘也可以使用True代替数字1
End Sub
另一个有用的事件是Open事件,使用该事件确保显示报表前装载了重要的元素。示例略。
一些有用的报表和窗体级事件:Click、Close、Current、Load、NoData、OnFormat、Open。
(4)在Word中的文档级事件
在Word中,需要为特定的文档创建类模块来处理事件。也能应用该方法到全局模板或者制作加载项来控制应用程序级的事件。
使用WithEvents关键词声明能在工程中使用的公共的Word对象。
首先,需要在工程中添加一个类模块并将其命名,本例中为clsEvents。然后,打开代码窗口,输入下面的代码:
Public WithEvents appWrd    As Word.Application
Private Sub appWrd_DocumentChange()
End Sub
第一行代码指定Word应监控的事件。下面的过程表明应该监控文档变化。
事实上,此时并不会发生什么。然而,可以使用文档级的Open事件来为Word设置应用程序级事件。
Dim mappWrd As New clsEvents
Private Sub Document_Open()
    Set mappWrd.appWrd = Application
End Sub
一旦这样处理后,准备使用在类模块clsEvents中的事件。记住,必须首先编写所有的事件,否则类将被中止,因为没有监控的事件。
下面添加一段实用的事件代码,用来追踪某文档的打印:
Private Sub appWrd_DocumentBeforePrint(ByVal Doc As Document, _
    Cancel As Boolean)
    If Doc = ThisDocument Then
        'code
    Else
        'code
    End If
End Sub
注意,WithEvents变量appWrd将监控整个Word应用程序,因此当试图只监控包含代码的文档时,应该创建代码使之仅在正确的文档中执行。在示例中,使用ThisDocument对象来确保仅在包含代码的文档中有效。
(5)应用程序级事件
正如在Word中所见,如果打算配置应用程序级的事件,则需要使用类模块。因此,这里所有的示例都使用类模块。并且,使用标准的名称clsEvents。
第一个示例演示如何监控特定的工作簿的打印,并且仅允许打印指定的工作簿。
(1)打开Excel,在VBE中添加一个新的类模块。
(2)在类模块的代码窗口中,输入下面的代码:
Public WithEvents appXL As Excel.Application
Private Sub appXL_WorkbookBeforePrint(ByVal Wb As Workbook, _
    Cancel As Boolean)
    If Not Wb.Name = "AllowedWorkbook.xlsm" Then
        MsgBox "不允许打印,除非是工作簿AllowedWorkbook.xlsm", vbCritical
        Cancel = True
    End If
End Sub
(3)在应用程序的Open事件中,输入下面的代码:
Dim mappXL As New clsEvents
Private Sub Workbook_Open()
    Set mappXL.appXL = Application
End Sub
此时,将只允许指定的工作簿被打印。
第二个示例在Access中,在数据输入到表之前提供一种验证数据的方式。假设一个文本框用于输入数据到某字段,并且仅包含正值,因此,如果该文本框出现在多个窗体中时,可能试图为每个文本框编写验证代码。然而,可以在类模块中编写简单的过程然后将其在工程中使用,从而节省时间。代码如下:
Public WithEvents clsTextBox As TextBox
Private Sub clsTextBox_AfterUpdate()
With Me
    If .clsTextBox.Value<0 Then
        MsgBox “该字段不允许输入负值……”,vbCritical
        .clsTextBox.Value=0
    End If
End With
End Sub
创建类模块后,需要从使用文本框的任何窗体调用该模块。注意,在每个窗体中文本框必须有相同的名称。从设计视图中打开一个可用的窗体并输入下面的代码:
Dim mtxtbox As New clsTextBoxEvents
Private Sub Form_Load()
Set mtxtbox.clsTextBox=Me.txtValue
End Sub
Private Sub txtValue_AfterUpdate()
‘该过程没有使用,仅确保执行类
End Sub
现在,应该能够在工程中的任何地方重复使用该类而无须重复编写相同的代码。这是一项非常有用的技术,能应用到许多解决方案中。

TA的精华主题

TA的得分主题

 楼主| 发表于 2008-4-19 10:45 | 显示全部楼层
4、对象浏览器
(1)找到对象及其属性、方法和事件的最好方式是使用对象浏览器。
(2)在VBE中,按F2键即可快速打开对象浏览器。
5、引用库
一旦引用相应的库之后,就能够查看其对象模型获得帮助,也能够使用该库的对象及其属性和方法。
使用VBE菜单“工具”─—“引用”,打开“引用”对话框,其中列出了所有可引用的库。
(1)介绍早期绑定和后期绑定
当使用外部对象时,也就是说使用不属于正处理的对象模型中的对象时,必须创建该外部对象的实例。即使用早期绑定或后期绑定。
当首先安装对其他库的引用时,使用的是早期绑定。早期绑定不仅加速了编写代码的过程(现在所有关于该库的对象包括其属性和方法都公开),而且改善了代码的性能。缺点是使用该程序代码的用户也需要已经注册了所引用的库。如下面的代码:
Dim olEmail As Outlook.MailItem
后期绑定不依赖于先引用库,而且首先简单地声明普通的对象并使用其属性和方法,在运行时再调用相关的库。例如下面的代码:
Dim appOL As Object
Dim Email As Object
Set appOL=CreateObject(“Outlook.Application”)
Set Email=appOL.CreateItem(olMailItem)
CreatObject函数总是创建对象的新实例,如果已经打开了该对象的一个实例,那么将创建另一个实例。如果不希望由于创建应用程序对象新实例而耗尽资源,可以使用GetObject函数:
Set appWrd=GetObject(,”Word.Application”)
如果已经打开则该代码将获取Word的application对象,因而不需要创建该应用程序的另一个实例。当然,如果该应用程序的实例不存在,那么代码将返回错误。可以使用下面的代码进行处理并避免错误:
On Error Resume Next
Set appWrd=GetObject(,”Word.Application”)
If appWrd Is Nothing Then Set appWrd=CreateObject(“Word.Application”)
使用On Error Resume Next忽略可能没有已经运行的实例而产生的错误。如果发生错误,则不会设置该对象并且仍然是Nothing。因此,随后的语句检查是否是nothing,如果是则创建该对象的一个新实例。

TA的精华主题

TA的得分主题

 楼主| 发表于 2008-4-19 10:45 | 显示全部楼层
调试代码
(1)Debug.Print和Debug.Assert
Debug对象用于向立即窗口输出结果,它有两个方法:
Print:用于在立即窗口打印输出结果。
Assert:当预料的结果应该评估为True时,用于断言在运行时评估为False的条件。当感到代码应该返回true值或者基于true的假设,但事实上返回false值,则当其评估为false时可以断言该代码,并停留在该断言行,如下例所示:
Sub debugAssert()
    Dim lngRow As Long
    Dim blnAssertNotEmpty As Boolean
    lngRow = 1
    Do Until IsEmpty(Cells(lngRow, 1))
        blnAssertNotEmpty = Not (IsEmpty(Cells(lngRow + 1, 1)))
        lngRow = lngRow + 1
        Debug.Assert blnAssertNotEmpty
    Loop
End Sub
本例中,将在工作表指定的单元格中循环,检查是否下一个单元格是空。当遇到空单元格时,Boolean值将为False,代码停止在断言行。
(2)Stop语句
Stop语句用于执行代码时在特定的点挂起。假设希望在打开工作簿、文档、报表或窗体时停止代码。可以添加MsgBox并输入在显示消息之后的代码。然而,这会导致不期望的中断,在每次执行该行代码时不得不处理消息框。此时,一种更好的选择是使用Stop语句,避免消息框。
使用Stop语句将延缓执行,能使您分析代码。例如,下面的代码包括Stop语句,在设置Ribbon对象之前中止过程。如果在这里停止执行,那么UI仍然会装载,但Ribbon对象将不可用:
Sub rxIRibbonUI_onLoad(ribbon As IRibbonUI)
    Stop
    Set grxIRibbonUI = ribbon
End Sub
注意,使用Stop与在代码中添加断点有相似的效果。然而,当文件关闭后,断点也失效了。
(3)立即窗口
立即窗口是一个有价值的工具,可以:1)测试所编写的代码或者调试代码中的问题;2)查询或更改变量的值;3)当中止执行时,查询或更改变量的值;4)为变量赋新值;5)查询或更改属性值;6)运行子过程或函数;7)查看调试输出。
(4)本地窗口
本地窗口用于显示正执行过程的变量值和对象。如果需要监控多个变量、值或对象,那么使用本地窗口可能是最好的方式之一。
(5)监视窗口
监视窗口是调试代码的另一个极好的工具,能够指定监视某对象的值的指令,例如变量、表达式、函数调用,等等。当条件满足或者正监视的变量改变时,监视窗口将暂停代码的执行。

TA的精华主题

TA的得分主题

 楼主| 发表于 2008-4-19 10:46 | 显示全部楼层
错误处理
有一些简单的方式来处理未解决的或不可预知的问题,错误处理技术就是一种,能够帮助您建立更健壮的代码。
(1)On Error Resume Next
这是最简单且最频繁使用的错语处理语句。该语句将简单地忽略发生错误的语句并继续执行下面的代码。但是,如果其他代码行依赖于该行出错的语句,那么程序将会出现一连锁的错误反应,因而得不到正确的结果。
建议保守地使用这种错误处理的方法。
(2)On Error GoTo
这种形式的错误处理有:
On Error GoTo 0或On Error GoTo <label>,label代表选择的标记,如ErrHandler或Err_Handler。
Sub OnErrorGoTO()
    On Error GoTo Err_Handler
    '这里是过程/函数的主体
    Exit Sub  '或Exit Function
Err_Handler:
    '这里是错误处理代码
    Resume Next
End Sub

TA的精华主题

TA的得分主题

 楼主| 发表于 2008-4-19 10:47 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册

处理数组
数组基本上是一组被索引的数据,VBA将其作为单个的变量。例如:
Dim InflationRate(2000 To 2007) As Double
索引值是2000至2007之间的年份。
(1)确定数组的边界
使用LBound和UBound函数确定数组的上边界和下边界(上限和下限)。
(2)调整数组的大小
即创建动态数组。此时,使用ReDim关键字来调整数组的大小。
但是,如果数组已经包含值,那么使用ReDim调整数组的大小将会清除数组内容。如果想保留数组中以前的信息,则可以在ReDim关键字后再使用Preserve关键字,例如:
ReDim Preserve InflationRate(StartingYear To EndingYear)
Dim InflationRate() As Double
Dim varArray As Variant
Sub RunExampleArray()
    Call ExampleArray(1990, 2000)
    Call ExampleArray2(1980, 2007)
End Sub

Sub ExampleArray(ByVal StartingYear As Long, _
    ByVal EndingYear As Long)
    ReDim InflationRate(StartingYear To EndingYear)
    InflationRate(1990) = 2.4
    InflationRate(2000) = 1.9
    varArray = InflationRate()
End Sub

Sub ExampleArray2(ByVal StartingYear As Long, _
    ByVal EndingYear As Long)
    ReDim InflationRate(StartingYear To EndingYear)
    InflationRate(1990) = varArray(1990)
    InflationRate(2000) = varArray(2000)
    Debug.Print LBound(InflationRate)
    Debug.Print UBound(InflationRate)
    Debug.Print InflationRate(1990)
    Debug.Print InflationRate(2000)
End Sub

下一章:回调的具体细节。

TA的精华主题

TA的得分主题

 楼主| 发表于 2008-4-19 14:34 | 显示全部楼层

RibbonX:自定义Office 2007功能区
第五章 回调:对自定义UI添加功能的关键

回调,即使定制能够工作的代码。如果没有回调,UI可能看起来是漂亮的,但只是一个漂亮的Ribbon。当然,除了从内置的控件不需要自定义回调外。
下面介绍XML与VBA组合。需要在XML代码中指定回调,然后编写VBA代码来匹配和处理回调。

TA的精华主题

TA的得分主题

 楼主| 发表于 2008-4-19 14:34 | 显示全部楼层
一、回调是什么,为什么需要回调
回调是子过程和函数,被自定义UI使用,使得定制的UI能够工作。例如,当设置某按钮的onAction属性并装载时,一旦单击该按钮,就产生回调,即在属性中指定的操作。如果没有找到指定的操作,那么回调将失败,因为代码中发生了例外,即在属性中指定的回调在VBA中不存在。
使用自定义UI中的功能时,UI将调用VBA项目,搜寻指定的函数或过程。如果找到,则将其值传回UI;如果没有发现指定的函数或过程,那将产生错误的结果。
有两种主要的方式来创建回调:
(1)直接在VBE的标准模块中输入过程或函数;
(2)使用工具生成过程,例如Office 2007 CustomUI Editor。
使用工具,例如Office 2007 CustomUI Editor的主要优势之一是能返回一个回调签名,也称作subprocedure stub,对于XML代码中每个属性都有一个回调需要处理,接受回调作为值的属性包括onAction、getVisible和getImage。
使用CustomUI Editor还能避免错误,节省时间。
当项目装载且需要访问某些回调并运行,意味着如果回调处理(即响应回调的VBA代码)在该项目中不存在,则将产生错误消息。

TA的精华主题

TA的得分主题

 楼主| 发表于 2008-4-19 14:36 | 显示全部楼层
二、为动态回调建立文件
为了使用自定义控件工作,文件必须启用宏,否则不能添加或运行VBA代码。
1、捕获IRibbonUI对象
IRibbonUI对象引用Ribbon用户界面。
在VBA中使用的关键之一是使整个Ribbon对象无效(因而能改变Ribbon的某项特征)或者使Ribbon中指定的控件无效(因而能改变该控件的某项特征)。
(1)调整XML来包括onLoad
为了使用IRibbonUI对象,需要在VBA中对其设置。此时,需要对UI的onLoad属性指定值,这能通过为onLoad属性指定回调来实现。
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="rxIRibbonUI_onLoad">
因为onLoad属性的值是一个回调,所以需要在VBA代码中对其进行处理。接着,使得在工程中能够使用IRibbonUI对象。
(2)创建VBA代码处理onLoad事件
可能注意到,以前称onLoad为属性,而现在将其称为事件。这是因为在XML文件中,能为许多属性定义值,例如onAction、getLabel和onLoad。一旦为某属性赋值,如果能以某种方式触发,将导致事件发生。
因为IRibbonUI对象用于应用程序,所以需要在标准模块的全局声明部分对其声明。
Dim grxIRibbonUI As IRibbonUI
'Callback for customUI.onLoad
Sub rxIRibbonUI_onLoad(ribbon As IRibbonUI)
    Set grxIRibbonUI = ribbon
End Sub
注意,此时在对代码作出变化后,需要保存、关闭并重新打开工程,才能使改变生效。

TA的精华主题

TA的得分主题

 楼主| 发表于 2008-4-19 14:37 | 显示全部楼层
三、生成第一个回调
─—如何知道使用哪个回调签名
─—例如,toggleButton有下列签名:
Sub rxtgl_click(control as IRibbonControl,pressed as boolean)
一个普通按钮的签名:
Sub rxbtn_click(control as IRibbonControl)
1、从头开始编写回调
如果知道了回调签名,则不必使用标准的形式声明参数。例如,有一个使用下列XML的toggleButton:
<toggleButton
id=”rxtgl”
label=”Toggle”
size=”large”
onAction=”rxtgl_click”
imageMso=”FormatPainter”/>
编写rxtgl_click回调如下:
Sub rxtgl_click(rxctl As IRibbonControl,toggled As Boolean)
If toggled Then
    MsgBox “I am toggled …And my ID is “ & rxctl.ID,vbInformation
End If
End Sub
注意,无论何时都必须是“正确类型的正确参数”。
也能更改onLoad回调签名来满足需要,如下:
Sub rxIRibbonUI_onLoad(MyRibbon As IRibbonUI)
Set grxIRibbonUI=MyRibbon
End Sub
但是,使用标准的签名及其参数,使他人更容易理解。
表:不同对象属性的回调签名
属性                   回调签名
onLoad                 (ribbon as IRibbonUI)
getLabel,getPressed,
getEnabled,getImage,
getScreentip,getVisible,etc  (control as IRibbonControl,ByRef returnedVal)
onAction(对toggleButton)  (control as IRibbonControl,pressed as Boolean)
onAction(对按钮)         (control as IRibbonControl)
2、使用Office CustomUI Editor生成回调
(1)在CustomUI Editor中打开包含XML代码的Excel或Word文件。(对Access文件,复制并粘贴XML代码到空的CustomUI Editor代码窗口)
(2)单击“Generate Callbacks”按钮。
(3)出现一个新的Callbacks选项卡。
(4)复制并产生的代码粘贴到VBA工程中。
下图为使用CustomUI Editor生成回调的示例:

3、理解文件打开时事件的顺序
表:当打开文件后选项卡获取焦点时事件顺序
事件        选项卡获取焦点    选项卡获取焦点    按下ALT键    鼠标在其上方
onLoad      最顶级的事件,当装载UI时发生
getVisible       1                 1                N/A           N/A
getLabel        2                 3                N/A           N/A
getImage        3                 4                N/A          N/A
getEnabled      4                 2                N/A           N/A
getKeytip      N/A               N/A               1             N/A
getScreentip    N/A               N/A              N/A            1
getSupertip    N/A                N/A              N/A            2
4、能够有具有相同名称但不同签名的两个回调吗?
VBA不允许在相同项目中的两个回调使用相同名称但不同的签名。
然而,如果回调在不同的工程中,则相同的回调名称能够有不同的签名。因此,如果同时有一个以上的Word和/或Excel文档打开,可能发现回调返回不可预料的结果。因为如果多个操作与一个回调名称相联系,那么将运行当前文档的回调。
假设打开一个安装了两个加载项的Excel工作簿,三个项目都有一个带有称为rxbtnnsQaShared的控件,当单击UI中该控件时,所期望的是添加一个新工作簿,然而将出现一个消息框。检查后,发现为所有三个按钮都使用了相同的回调。此时,可以使用断点调试,看看单击操作调用了哪个回调。
上述表明,只要在不同工程中,虽然允许使用带有不同签名的一致名称,但这不是一个好主意。

[分享]《RibbonX:自定义Office 2007功能区》阅读笔记

[分享]《RibbonX:自定义Office 2007功能区》阅读笔记

TA的精华主题

TA的得分主题

 楼主| 发表于 2008-4-19 14:38 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
四、调用位于不同工作簿中的过程
与上面所讲的情况相似,如果XML代码运行位于不同工作簿中的VBA,也会碰到类似的问题。
假设有两个工作簿:Book.xlsm和Book2.xlsm,希望在第一个工作簿中添加一个按钮,运行第二个工作簿中的过程,那么使用下面的XML代码创建UI:
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
    <ribbon>
        <tabs>
            <tab id="rxtabDemo"
                  label="My Custom Tab"
                  insertBeforeMso="TabHome">
                  <group id="rxgrpDemo"
                         label="My Demo Group">
                         <button id="rxbtnDemo"
                                 label="My Demo Button"
                                 size="large"
                                 onAction="Book2.xlsm!rxbtnDemo_click"
                                 imageMso="FileStartWorkflow"/>
                  </group>
            </tab>
        </tabs>
    </ribbon>
</customUI>
在Book2.xlsm的标准模块中,放置下列代码:
Sub rxbtnDemo_click(control As IRibbonControl)
    MsgBox "您调用了位于:" & ThisWorkbook.Name & "中的过程."
End Sub
注意,必须要打开工作簿。
如果希望onAction属性指向加载项,那么简单地在onAction代码名称前使用[add-in name].xlam前缀,例如:
onAction=”myAddIn.xlam!rxbtnDemo_click”
然后,在加载项模块中放置回调VBA代码:
Book3.xlam!rxbtnDemo_click
此时,必须加上前缀xlam!。注意,如果在UI的onAction属性下指定的过程中xlsm和xlam有相同的名称,则该事件会运行包含在活动工作簿中的代码。
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

关闭

最新热点上一条 /1 下一条

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-4-25 06:43 , Processed in 0.045462 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表