说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

改变VCL的行为--一个使用可视化元件的实例-Delphi资料

HTML文档下载 WORD文档下载 PDF文档下载
改变VCL的行为--一个使用可视化元件的实例-Delphi资料

要使一个可视化控件的行为与其默认行为不同,我们通常要从这个原始类继承,创建一个新的控件。本文将介绍如何在不创建新类的情况下动态改变原生Delphi可视化控件的行为。

这可能实现吗?秘密在于在控件之前抢先截获Windows消息。这可以通过使用一个叫做WindowProc的TControl属性来实现,这个属性实质上指向控件的Windows消息事件处理器(event handler)。

为了展示这一技术,我们将创建一个LinkedLabel控件,可以将它连接到任何TControl控件并且动态改变它的行为。TLinkedLabel由TLabel继承而来,附加4个公开的属性:

? Associate —— 将被改变行为的相连控件

? CapsLock —— 当这个Boolean属性被设置为True时,特定类型的控件将把小写键盘输入作为大写来处理。这个属性并不对所有控件有效,因为并不是所有的控件都以相同的方式相应WM_CHAR消息。经测试Edit,MaskEdit,Memo,和RichEdit控件都对CapsLock属性有响应,但是ComboBox则不响应。很明显,CapsLock属性对于很多其他控件(如Button、CheckBox等)只有很小的影响,或者没有影响。

? Gap —— LinkedLabel与相连控件的距离

? OnTop —— 这个Boolean属性决定LinkedLabel出现在相连控件的左侧还是顶端。

另外,TlinkedLabel将保持自身和相连控件的Enabled和Visible属性相一致。它也会保持自身和相连控件的距离和角度,也就是说,当你移动LinkedLabel时,其关联也会随之移动,反之亦然。

我们来看一下TLinkedLabel类的声明,如图1所示。

unit LinkedLabel;

interface

uses

Messages

Classes

Controls

StdCtrls;

type

TLinkedLabel = class(TLabel)

private

// 相连控件.

FAssociate: TControl;

// 将 FAssociate 置为全大写模式

FCapsLock: Boolean;

// 标签与关联控件之间的距离

FGap: Integer;

// 标签在关联控件顶端时为true

FOnTop: Boolean;

// 保存 FAssociate.WindowProc的原始值

FOldWinProc: TWndMethod;

// 用于防止无限更新循环

FUpdating: Boolean;

protected

procedure Adjust(MoveLabel: Boolean);

procedure SetGap(Value: Integer);

procedure SetOnTop(Value: Boolean);

procedure SetAssociate(Value: TControl);

procedure NewWinProc(var Message: TMessage);

procedure Notification(AComponent: TComponent;

Operation: TOperation); override;

procedure WndProc(var Message: TMessage); override;

public

constructor Create(AOwner :TComponent); override;

destructor Destroy; override;

published

property Associate: TControl

read FAssociate write SetAssociate;

property CapsLock: Boolean

read FCapsLock write FCapsLock;

property Gap: Integer read FGap write SetGap default 8;

property OnTop: Boolean read FOnTop write SetOnTop;

end;

现在让我们来仔细看看这个控件中的不同方法,先由构造器(constructor)开始。首先说明一下,当创建一个新对象时,与它相关联的所有内存都被清空。这个动作将会自动把Fassociate和FoldWinProc设置为nil,将FcapsLock、FonTop、Fupdating设置为False。所有这些都不需要在构造器中明确的初始化它们。因此,唯一需要我们在构造器中设置的就是Gap的默认值。

implementation

constructor TLinkedLabel.Create(AOwner: TComponent);

begin

inherited;

FGap := 8;

end;

现在我们来看一下Adjust方法,它负责安排LinkedLabel或者关联控件的放置(取决于MoveLabel参数的取值)。正如你将在代码中看到的,LinkedLabel与相关控件的实际位置取决于Gap和OnTop属性(见图2)。虽然我们在OnTop中只提供了两种可能的选择,不过可以很容易的对其编程以提供更多的可能性。不过,把TlinkedLabel武装到牙齿(原文是“add a lot of "bells and whistles"”,译者注)并不是本文的重点,这项任务就委托给读者们来完成吧。

procedure TLinkedLabel.Adjust(MoveLabel: Boolean);

var

dx

dy: Integer;

begin

if (Assigned(FAssociate)) then begin

if (FOnTop) then

begin

dx := 0;

dy := Height + FGap;

end

else

begin

dx := Width + FGap;

dy := (Height - FAssociate.Height) div 2;

end;

if (MoveLabel) then

begin

Left := FAssociate.Left - dx;

Top := FAssociate.Top - dy;

end

else

begin

FAssociate.Left := Left + dx;

FAssociate.Top := Top + dy;

end;

end;

end;

现在,我们来完成Gap和OnTop属性的set方法(见图3),以便当Gap或者Onop属性被修改时我们可以改变LinkedLabel的位置。

procedure TLinkedLabel.SetGap(Value: Integer);

begin

if (FGap <> Value) then

begin

FGap := Value;

Adjust(True);

end;

end;

procedure TLinkedLabel.SetOnTop(Value: Boolean);

begin

if (FOnTop <> Value) then

begin

FOnTop := Value;

Adjust(True);

end;

end;

现在是SetAssociate方法

procedure TLinkedLabel.SetAssociate(Value: TControl);

begin

if (Value <> FAssociate) then begin

if (Assigned(FAssociate)) then

FAssociate.WindowProc := FOldWinProc;

FAssociate := Value;

if (Assigned(Value)) then

begin

Adjust(True);

Enabled := FAssociate.Enabled;

Visible := FAssociate.Visible;

FOldWinProc := FAssociate.WindowProc;

FAssociate.WindowProc := NewWinProc;

end;

end;

end;

为了便于理解,我们需要详细的讨论一下WindowProc属性。WindowProc被定义为TwndMethod类型。TwndMethod可以在Controls单元中找到,定义如下:

TWndMethod = procedure(var Message: TMessage) of object;

注意,FoldWinProc同样被定义为TwndMethod,并且NewWinProc方法拥有与TwndMethod相同的参数结构。这就允许我们将FoldWinProc指向WindowProc的当前值,并把WindowProc重定向到NewWinProc方法。如果WindowProc只是另一个事件属性的话,我们为什么需要使用FoldWinProc呢?因为WindowProc与其它事件属性的不同之处在于WindowProc指向一个已经存在的事件处理器。如果我们只是简单的将WindowProc指向我们的方法,这个控件将不能再对任何Windows消息产生响应。为了解决这个问题,我们在把WindowProc指向NewWinProc之前把FoldWinProc设置为WindowProc的当前值。

在NewWinProc中,我们通过FoldWinProc调用原先的消息处理器(message handler),并且处理特定的Windows消息。因为我们修改了关联控件的WindowProc值,因此要在把关联改变到一个新的控件之前恢复它从前的取值。

避免把关联控件的WindowProc属性指向一个不再存在的例程也同样重要。如同我们所见的,在析构器中调用SetAssociate(nil)将会把WindowProc恢复为初始值。

destructor TLinkedLabel.Destroy;

begin

SetAssociate(nil);

inherited;

end

另外,我们也不希望关联到一个不再存在控件。通过覆盖Notification方法,我们可以知道关联组件何时被销毁,从而重置关联的指针:

procedure TLinkedLabel.Notification(AComponent: TComponent;

Operation: TOperation);

begin

if ((Operation = opRemove) and

(AComponent = FAssociate)) then SetAssociate(nil);

end;

现在我们来看NewProc方法。这里,我们只是寻找发送给关联控件的特定Windows消息。认识到这一点是很重要的:虽然方法通过关联控件调用,但它实际上是LinkedLabel的一部分,例如,Self=LinkedLabel,而不是关联控件。这对为一个按钮创建onclick事件处理器来说也是一样的,onclick事件处理器是作为按钮父窗体的一部分,而不是扩充Tbutton类的新方法。

procedure TLinkedLabel.NewWinProc(var Message: TMessage);

var

Ch: Char;

begin

if (Assigned(FAssociate) and (not FUpdating)) then begin

FUpdating := True;

try

case(Message.Msg) of

WM_CHAR:

if (FCapsLock) then begin

Ch := Char(TWMKey(Message).CharCode);

if (Ch >= ’a’) and (Ch <= ’z’) then

TWMKey(Message).CharCode := ord(UpCase(Ch));

end;

CM_ENABLEDCHANGED:

Enabled := FAssociate.Enabled;

CM_VISIBLECHANGED:

Visible := FAssociate.Visible;

WM_SIZE

WM_MOVE

WM_WINDOWPOSCHANGED:

Adjust(True);

end;

finally

FUpdating := False;

end;

end;

FOldWinProc(Message);

end;

如果你检查一下这个例程,就会发现我们并没有花多少力气去处理Windows消息。我们只注意几个特定的消息,然后就让关联通过调用FOldWinProc正常的处理它们。在处理WM_CHAR消息的时候,我们对消息的一部分做了改变,让控件认为我们按下的是大写字母键。

最后,我们关心一下两个不同的消息,以确定关联控件是否被移动了。这样做的原因在于从TwinControl继承的控件会在它们被移动时接到WM_MOVE消息,而此时其它的可视控件(如一个标签)则会收到WM_WINDOWPOSCHANGED消息。程序也检查了WM_SIZE消息,原因是如果OnTop属性为False,则LinkedLabel的位置会随控件的高度而变化。

我们这个控件的最后一个方法是:当LinkedLabel被改变时,要在关联的什么地方作修改?当然我们不使用覆盖Tlabel的现存方法来实现它,而是要用修改关联行为的相同技术来做。注意我们不是重新定向WindowsProc属性,而是覆盖了WndProc方法。为什么把它们叫做相同的技术呢?如果你看一下TControl的构造器,你可以发现WindowProc会被初始化以指向WndProc方法。所以从本质上讲,我们覆盖的是同一种方法,不过做得更“干净”,也不用去保存WindowProc的初始值。

procedure TLinkedLabel.WndProc(var Message: TMessage);

begin

if (Assigned(FAssociate) and (not FUpdating)) then begin

FUpdating := True;

try

case(Message.Msg) of

CM_ENABLEDCHANGED: FAssociate.Enabled := Enabled;

CM_VISIBLECHANGED: FAssociate.Visible := Visible;

WM_WINDOWPOSCHANGED: Adjust(False);

end;

finally

FUpdating := False;

end;

end;

inherited;

end;

对于刚刚完成的控件还有最后一点需要注意。你也许发现NewWinProc和WndProc中都使用了Fupdating。这个变量被用来通知LinkedLabel和它的关联控件其它控件正在发生改变。如果你忽略了这一步,很容易造成一个无限的更新循环,或者其它无法预料的结果。下面是一个事件流程,显示为什么需要Fupdating变量。

? 用户把 LinkedLabel 拖动到一个新位置。

? WndProc 接收到一个 WM_WINDOWPOSCHANGED 消息,并且触发 Adjust(False) 来移动关联控件。

? 作为对关联控件调整的一部分,Adjust 把FAssociate.Left设置为新值。

? FAssociate 触发 WM_MOVE 消息,指出它已经改变了位置。

? NewWinProc 监测到 WM_MOVE 消息并调用 Adjust(True) 以修改 LinkedLabel 的位置配合关联控件的移动。

如你所见,在关联控件试图移动LinkedLabel之前我们没有什么机会改变关联控件的Top属性来配合LinkedLabel的新位置。通过使用Fupdating变量,关联控件不会注意到WM_MOVE消息,也不会试图调用Adjust来重新布置LinkedLabel。

一对问题

在这篇文章中我没有提及TlinkedLabel的一对问题。下面是对它们的大致说明:

? 如果你把两个或者两个以上LinkedLabel关联到同一个控件然后释放它们之中的一个或者几个,就可能导致各种各样的问题。你可能会打断到其它LinkedLabel的关联,甚至可能导致被关联控件的WindowProc指向一个并不存在的历程。

? 如果你把 LinkedLabel 关联到另一个窗体上的控件,那么Notification 方法在那个控件被销毁时不会被调用。当控件被关联时调用 FreeNotification 可以解决这个问题,但这并没有真正指出问题所在。真正的问题在于我们允许它被关联在其它窗体的控件上。其实我们真正想实现的是把LinkedLabel与拥有相同Parent的控件相关联。虽然这么做并不难,不过要只在对象查看器的Associate属性下拉列表中显示符合条件的控件也需要一些小技巧。

结论

其实结论也没多少东西。替换现存控件的WindowProc确实有它的局限性,不过这毕竟是一种非常有用的技术。我想不出什么其它合适的方法来创建一个像TlinkedLabel这样的控件,让关联控件在被移动时也一并移动LinkedLabel。我可不想去尝试并且列出这种技术其它可能的用法,因为这种可能性是无限的,它只会被一个程序员的灵活性所局限。

《近匠》棱镜:手游渠道SDK平台的技术历程 OpenSSL究竟为何物,为何它的影响力如此之大? Redis大冒险:如何跳出SQL这个坑 【走近院士】张尧学:基于透明计算的云操作系统 微软转型之路:从Build 2014开始 大势所趋 HTML5成Web开发者最关心的技术 从火种到核心,浅析Hadoop大数据用户的演变 搭建高可用的MongoDB集群(上):MongoDB的配置与副本集 软硬件协同创新,共建未来数据中心 优秀Unix管理员的七个习惯 2014Unity亚洲开发者大会倒计时 干货内容日程汇总 TIOBE 2014年4月编程语言排行榜:Perl跌至历史最低点 Hadoop集群环境下网络架构的设计与优化 CloudFoundry架构优化:NATS集群化方案 Dropbox推独立应用,公司估值已达100亿美元 【走近院士】梅宏:云计算时代软件技术发展需求和挑战 云计算战争:OpenStack vs. VMware Cortana与Siri、Google Now的较量:支持功能更多 代码面试最常用的10大算法 “颠覆医疗” 时云医疗推三款硬件产品 华为章宇:如何学习开源项目及Ceph的浅析 EMC:大数据时代中对于数据保护的重新定义 Google Glass于4月15日在美对外开放购买,售价为1500美元 电脑上的网址怎么样复制到手机 创客天下——《Make》及Maker Faire创办人、O&amp;#039;Reilly Media创始人Dale Dougherty专访 《近匠》aGlass团队:透析眼控技术的价值 一周消息树:超级充电器来袭,30秒可为手机充满电 北森CTO张庆化:利用Spark实现数据驱动价值 大公网冯咀志:站内搜索系统的PowerLinux迁移实践 2014移动开发者必备的十大应用测试工具 前《连线》主编Chris Anderson:创客就要DIT 在URL地址栏中传递字符串参数遇到的一个问题??? 如何在繁體下打開簡體文件名的文件?要求不能用改名的辦法 插入数据到数据库! 高手关注--------送分-----------急急急! 在ASP中如何编程建立索引服务的编录及目录? 在web server application把MEMO字段中带格式(RTF)的内容转换成纯文本? 请问在身份验证中,能否有两个login.aspx??? 急~那篇给vb控件打包文件添加数字签名以便浏览器浏览的那篇文章在哪里? 用vb如何实现模拟雷达界面??欢迎赐教! 如何用CSS控制INPUT的输入外观不是立体的,而看起来是平板的样子,只有一个边线? 用户注册的问题,密码总验证不对,能不能看看错在哪里? 如何使文本框只显示下面的边框? xsl求助~~~ 怎么将int转为CString 有的论坛采用从数据库得到帖子,有的使用shtml,有的使用xml,孰优???还有其他吗? 100分==在JSP中打开word文件的实例 紧急在线等一个很简单的问题 DTPicker 怎样和数据库绑定? 如何动态改变VB报表中Section中的一个与数据环境绑定的rptTextBox的颜色!(100分求教) datediff()这个函数怎么用哪位高手可否给小弟讲解一下急用在线等 和女友分手了,痛苦! 谁有支持中文的带身份验证和邮件群发的ASP.NET邮件组件? 关于多个表通过相同的字段级联的问题,进来看看,一定给分! windowd server 2003许可证问题 谁有支持中文的带身份验证和邮件群发的ASP.NET邮件组件? 求合作伙伴——推广抗非典病毒的消毒剂!!! 在表分区中,按某个varchar2的字段的值和范围如何分呢 我又來了,不僅你們煩,我更煩. 服务器端与客户端的问题 服务器端与客户端的问题 最后的问题,谢谢帮我看看 发现MS WINDOWS版的C标准库一个在BUG!!!!! 在用VFP操作EXCEL时出现无法用字段内容填充单元格问题,请各位帮忙看看. 算法 救命! 有没有rmi的好例程,一用就灵,急 fast report 和 qreport 到底应该用哪一个???(来者有分!!) 关于“数据结构书”的回应——版主莫删 如何进入另一个对话框 在一字符串中怎样取出某一个字符出现的次数. 急! infopower 2000、 3000、4000 Password?? 自定下载的问题,请教高手 如何屏蔽播放.wav文件但找不到相应.wav文件时发出的默认声音? 如何打开另一个对话框 政治因素对我国军事专家的困扰[ZT] if ((btime==null)&&(etime==null )) 请教 一位女同事辭職,一位男同事說跟我有關,鬱悶中... 请问如何在B/S模式下,在页面上实现,如word,excel中的编辑功能? 如何画一个二元函数的图象 急!高分求教:Epson TM U300微型打印机打印作业丢失问题!(100分) 邻角相等的四边形是平行四边形吗?能做判定定理吗 今适有知而斯之的意思? 这些题有那几题是错的,求正确答案, 叽叽的,呱呱的,嗡嗡的等,算不算AAB式词语?为什么?叽叽的算不算两个词?(叽叽是一个,的是一个。) 一直忧虑科学家预言的小行星撞击地球,怎么都走不出来,怎么办?我一直忧虑科学家预言的小行星撞击地球,怎么都走不出来.有七八年的了,别笑话我,我真的就是怎么都不出.05年的一天在家看电 求第10题的解答过程,有错的请求正确答案急 学生扎辫子的方法,早上起来要自己扎的,好看一点的, 求问青蛙的什么地方能振动发声《物理》 日勺念什么字 平行四边形的对角相等吗? 青蛙靠哪里振动 一辆汽车从甲地开往乙地要10小时,一辆客车从乙地开往甲地要8小时.汽车先开出2小时后,客车再开出小时后与汽车相遇? 已知四边形一组对角相等,证明这个四边形是平行四边形 青蛙如何发声?它的叫声洪亮的原因? 甲,乙两地相距126千米,一辆汽车从甲地开往乙地,行驶了九分之七还多5干米,行驶了多少千米? 若函数y=x^2-4px-2的图像过点A(tanα,1)及B(tanβ,1),求sin2(α+β)的值2p/(1+p^2) 青蛙是怎样发声的``青蛙``蜜蜂``蟋蟀``蝉(知了)是怎样发声的`` 已知函数f(x)=sin(2x-π/3)(x∈R),给出如下结论:1、图像关于直线x=5π/12对称;2、图像的一个对称中心是(π/6,0);3、在[0,π/2]上的最大值为(根号3)/2;4、若x1,x2是该函数的两个不同 半径为5的圆O内有一点P,且OP=4则过点P的最短弦长为( ),最长弦长为( ) 观此可以知画理矣 理的意思 求角bdc=90度-1/2角a, 一组对边相等,一组对角相等的四边形____(填能或不能)判定为平行四边形.(求证明过程 ) 非知之难,行之为难;非行之难,终之斯难.不要典故,只要意思! (1)角BDC>角A (2)角BDC=角B+角A+角B 已知:tanα=3/4,tanβ=12/13且都是锐角,求α+β. 历史上最大的小行星撞击地球事件是在什么时候?直径有多大?威力有多大?是在寒武纪还是奥陶纪还是其他时候?注意是最大的小行星 如图,△ABC中,角B和角C的外角平分线交于点B,求证角BDC=90度-2分之1角A 四边形一组对角相等一组对边平行为什么不能判定平行四边形两个三角形AAS啊?为什么不是判定定理?求反例?那为什么不是判定定理啊?不能直接判定? 你怎么看待小行星撞击地球事件? 求帮我写马克思主义的作业最好提前完成. 为什么在判定平行四边形中,不能用对角相等直接判定?是两组对角都平行 怎么倒的(X1-X2)+3/X1-3/X2=(X1-x2)[1+3/X1X2] 马克思主义主要写什么? 已知三角形ABC为等边三角形,O为三角形内一点,AO=根号5,BO=根号3,CO=根号2,求角BOC. 若一元二次方程x^2+3x-1=0的两根分别为x1,x2,则x1+x2=-3,x1x2=-1,x1^2+x2^2=? 在△ABC中,CD⊥AB于D,BE⊥AC于E,CD与BE相交于H,且CH=AB,则AE=CE-BH,请说明理由 在三角形ABC中边AB=2,BC=1角ABC=60点O为AC上一点若BO=二分之根号七试确定O点位置 如某方程的两根都在区间(1,4)内那两根有什么限制条件?还有某方程一个根在(-2,0)内,另一个根在(1,3)内...什么叫内?两根又有什么限制条件? 秦始皇陵墓什么时候发掘?期待! 已知αβ都是锐角 cosα=4/5 tan(α-β)=﹣1/3求sin(α-β) 青蛙呱呱叫是由于它的鸣囊在 秦始皇的墓什么时候能挖掘? 初二历史专题知识归纳整理三方面 经济 思想文化 只要写时间 和事件就好了 如果答案给的好的话 一辆快客上午8:00从甲地开往乙地,到下午2:00正好走了全程的40%,这时汽车离乙(列式计算.)一辆快客上午8:00从甲地开往乙地,到下午2:00正好走了全程的40%,这时汽车离乙地还有252千米.问 秦始皇的陵墓什么时候才能挖掘 半径为5的圆O内有一点P,且OP=3则过点P的最短弦长为( ),最长弦长为( ) 等腰直角三角形,直角边长50cm,另外两角都是45度,求斜边长度 30°,60°,90°角的比是多少?知道一个六十度角的长度该怎么知道九十度和三十度角的长度 △ABC中,AB=AC,BD,CE分别平分∠ABC,∠ACB,AG⊥BD与G,AH垂直CE与F交BC于H,求证1.△AFG是等腰三角形.2.△CAH是等腰三角形. 为什么说森林是人类的朋友?请联系生活实际谈一谈 一个三角形,已知两角度数,和一条边长,求斜边?一个三角形,已知两角度数30度,60度,和底边长790,求斜边?我想要求斜边的过程,或是公式?? excel怎么把各选项卡中的数据集合到一起并求平均数如图,每个选项卡中都有数值,我需要把这些数值全部加起来并求和 有什么简便方法吗 系统解剖怎么学 姓项的有多少人 两组对角分别相等的四边形是平行四边形为什么没有作为判定定理 条件概率中的P(AB)如何计算? 马克思列宁有什么著作用什么语言写的?我个人觉得他们都是非常有思想见解独到的人,但不想看他们的思想经过技术改造的中译本,因此想学学他们的语言,看看他们真实的面貌.非常感谢二楼 判定定理2:两组对边分别相等的四边形是平行四边形判定定理3:对角线互相平分的四边形是平行四边形请用两种方法分别证明这两个定理 条件概率:P(A∩B|A)≠P(B)对不对.为什么?条件概率的问题.P(A∩B|A)≠P(B) 这题错在哪,正确答案是: 两名美籍船员在尼日利亚遭绑架南桑威奇群岛东部25日发生6.5级地美油轮在尼日利亚遇袭 两人被海盗劫为美元对主要货币汇率24日继续在低位小纽约股市股指24日上涨纽约商品交易所黄金期货市场24日交投美国对中国和印尼产味精发起双反调查美“杀死中国人”节目被删 万人在白宫加拿大魁北克发现丰富稀土矿资源美国官员在社交网站说总统\"坏话\"深秋“平补”有讲究美19岁学生欲徒步闯南极美国少女做小首饰赚大钱大姐109 二姐106 小妹99 英愿广州人爱下厨的传统代代相传擅用其形象 中企遭控告韩泡菜、日和食将入非遗BBC主播再曝性侵英一“大学”批准小狗入学 缴费即获硕证据表明艾滋病儿或可治愈日本进入“胖女时代” 专家称或因对瘦释放新乡贤文化的正能量黄石新港口岸昨开关第二届乡贤论坛在江夏开幕资阳区:项目建设起高潮图文:也门恐袭夺命49条蒋超良王晓东会见伊利集团董事长潘刚鸡年扩容了是县域发展的灵魂积极转型升级,培育出口新优势湖北空手道瞄准东京奥运戴帽的C罗挽救了皇马奏响民生为本的最强音湖北日报讯 漫画/王铎坚持党的领导做好高校思想政治工作 用乒超联赛鄂军横扫鄂尔多斯队湖北空手道瞄准东京奥运警惕一味求快带来的暗伤中西部多地遭遇重度雾霾长沙西湖文化园地铁物业项目主体完成了解群众期盼 听取意见建议创新高校党建 精耕细种“责任田”
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘