第零步,测试代码:

  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3. Label1.Caption := 'Hello World';
  4. end;

---------------------------------------------------------------
第一步,先看TLabel的继承过程,及其关键属性:

  1. TControl = class(TComponent)
  2. protected
  3. property Caption: TCaption read GetText write SetText stored IsCaptionStored;
  4. property Text: TCaption read GetText write SetText; // 和Caption是一回事,别名而已
  5. property WindowText: PChar read FText write FText; // Windows窗口的真正标题
  6. end;
  7.  
  8. TGraphicControl = class(TControl)
  9. private
  10. FCanvas: TCanvas; // 私有内部画板,不用程序员申请就有了
  11. end;
  12.  
  13. TCustomLabel = class(TGraphicControl)
  14. public
  15. property Caption; // 变成公开属性,但不是发布属性
  16. end;
  17.  
  18. TLabel = class(TCustomLabel)
  19. published
  20. property Caption; // 变成发布属性
  21. end;

显然,最后调用的还是TControl.SetText;函数起了左右,也是真正的入口函数。

---------------------------------------------------------------
第二步,查看函数调用过程,发现分为两个消息步骤,先发消息设置文字,后发消息通知文字改变了:

  1. procedure TControl.SetText(const Value: TCaption);
  2. begin
  3. if GetText <> Value then SetTextBuf(PChar(Value)); // 类函数
  4. end;
  5.  
  6. procedure TControl.SetTextBuf(Buffer: PChar);
  7. begin
  8. Perform(WM_SETTEXT, 0, Longint(Buffer)); // 先发消息设置文字
  9. Perform(CM_TEXTCHANGED, 0, 0); // 文字设置完了,还要通知一下,TEdit,TLabel和TGroupBox都有相应的消息处理函数
  10. end;
  11.  
  12. // WM_SETTEXT消息一路传递,先在TLabel自己和各个祖先类里的WndProc检索,后开始查找自己和各祖先类WM_SETTEXT的消息函数,发现都没有处理,最后到这里才会被处理:
  13. procedure TControl.DefaultHandler(var Message);
  14. var
  15. P: PChar;
  16. begin
  17. with TMessage(Message) do
  18. case Msg of
  19. WM_GETTEXT: // 取得文字
  20. begin
  21. if FText <> nil then P := FText else P := '';
  22. Result := StrLen(StrLCopy(PChar(LParam), P, WParam - 1));
  23. end;
  24. WM_GETTEXTLENGTH: // 取得文字长度
  25. if FText = nil then Result := 0 else Result := StrLen(FText);
  26. WM_SETTEXT: // 设置文字
  27. begin
  28. P := StrNew(PChar(LParam));
  29. StrDispose(FText);
  30. FText := P; // 这里设置Caption
  31. SendDockNotification(Msg, WParam, LParam);
  32. end;
  33. end;
  34. end;

---------------------------------------------------------------
第三步,上面的函数合起来只是重新设置了TLabel的Caption属性文字,这还远远不代表什么。因为还需要显示它,这才是重头戏。因此TControl(也就是TLabel)马上发送了CM_TEXTCHANGED消息,并当场在TLabel类中就找到相应的消息函数:

  1. procedure TCustomLabel.CMTextChanged(var Message: TMessage);
  2. begin
  3. Invalidate; // 调用TControl.Invalidate;使其图像失效
  4. AdjustBounds; // 类函数,看看有没有必要调整大小和边框
  5. end;
  6.  
  7. // 这个函数基本上是图形控件使用的,因为TWinControl覆盖了这个函数,永远不会执行到这里来
  8. // 这个函数存在的意义是,让其它类函数简单调用,这里负责加上类的属性成员作为参数。起了一个桥梁和中介的作用。
  9. procedure TControl.Invalidate;
  10. begin
  11. // 图形控件默认不透明风格。但是新增标签的时候,默认就是不透明。
  12. InvalidateControl(Visible, csOpaque in ControlStyle); // important 刷新无效区域的时候,还要传递控件的不透明状态
  13. end;
  14.  
  15. // 非虚函数,私有函数,主要是决定是否使控件图像失效
  16. procedure TControl.InvalidateControl(IsVisible, IsOpaque: Boolean);
  17. var
  18. bParentOpaque: Boolean;
  19. bChlipped: Boolean;
  20. Rect: TRect;
  21. // 检测自己是否被完全掩盖(剪裁)
  22. function BackgroundClipped: Boolean;
  23. var
  24. R: TRect;
  25. List: TList;
  26. I: Integer;
  27. C: TControl;
  28. begin
  29. Result := True; // 默认不需要重画,直到发现自己有一部分需要重画
  30. List := FParent.FControls; // 专指父控件的图形子控件列表
  31. I := List.IndexOf(Self); // 从父控件的子控件列表里寻找自己。
  32. while I > 0 do
  33. begin
  34. Dec(I); // 根据子控件的兄长来计算自己是否需要重画。
  35. C := List[I];
  36. with C do
  37. if C.Visible and (csOpaque in ControlStyle) then // 如果可视并且不透明
  38. begin
  39. // 这些计算对Rect本身不影响
  40. IntersectRect(R, Rect, BoundsRect); // API,计算交叉区域,第二个参数是自己的矩形,第三个是兄弟的矩形
  41. if EqualRect(R, Rect) then Exit; // API,交叉区域与自己的矩形完全相等,即完全被覆盖就退出,也就是不用重画了
  42. end;
  43. end;
  44. Result := False; // 兄长都与其不相等,即有一部分需要重画,即背景没有被剪裁(或者没有被完全掩盖)
  45. end;
  46. begin
  47. // 要求显示 正处于组件设计状态 不是 设计期间不可视
  48. if (IsVisible or (csDesigning in ComponentState) and not (csNoDesignVisible in ControlStyle))
  49. // 父控件不为空 父控件有句柄
  50. and (Parent <> nil) and Parent.HandleAllocated then
  51. begin
  52. Rect := BoundsRect; // 类函数,简单计算(根据控件的长宽高)标签的坐标以及尺寸
  53. // 为了分析更清楚,我改成成以下语句:
  54. bParentOpaque := csOpaque in Parent.ControlStyle; // Form默认透明(csOpaque不在风格里)。但是父控件不一定是Form,不要思维僵化在这里。
  55. bChlipped:=BackgroundClipped; // 一般情况下,图形控件之间完全重合也是不可能的
  56. // 实验说明后两个一般情况下都是False,所以一般情况下只依赖于控件自己
  57. // 第三个参数为False,则保持背景不变。Not作用符以后,有三者条件之一成立即可,就会保持背景不变。
  58. InvalidateRect(Parent.Handle, @Rect, not (IsOpaque or bParentOpaque or bChlipped)); // API
  59. end;
  60. end;
  61.  
  62. procedure TCustomLabel.AdjustBounds;
  63. const
  64. WordWraps: array[Boolean] of Word = (0, DT_WORDBREAK);
  65. var
  66. DC: HDC;
  67. X: Integer;
  68. Rect: TRect;
  69. AAlignment: TAlignment;
  70. begin
  71. if not (csReading in ComponentState) and FAutoSize then
  72. begin
  73. Rect := ClientRect; // TControl的类属性,调用虚函数取得客户区(默认就是0,0,Width,Height)
  74. DC := GetDC(0); // API,参数0表示整个屏幕的DC
  75. Canvas.Handle := DC; // 给Label的canvas一个句柄,这样才能自绘
  76. // 根据三个参数(展开Tab的8个字符,是否换行)来计算所需区域
  77. DoDrawText(Rect, (DT_EXPANDTABS or DT_CALCRECT) or WordWraps[FWordWrap]); // 类保护函数,第一个参数是指针传递
  78. Canvas.Handle := 0; // 画完就不需要句柄了
  79. ReleaseDC(0, DC); // API
  80. X := Left;
  81. // 记录现在的左右对齐情况
  82. AAlignment := FAlignment;
  83. // 如有必要就颠倒左右对齐
  84. if UseRightToLeftAlignment then // TControl类函数,查看民族文字是左对齐还是右对齐
  85. ChangeBiDiModeAlignment(AAlignment); // Control单元的全局函数,颠倒原来的左右对齐
  86. // 如果是右对齐,那么重新计算文字的起点
  87. if AAlignment = taRightJustify then
  88. Inc(X, Width - Rect.Right);
  89. SetBounds(X, Top, Rect.Right, Rect.Bottom); // TControl的类函数
  90. end;
  91. end;
  92.  
  93. procedure TCustomLabel.DoDrawText(var Rect: TRect; Flags: Longint);
  94. var
  95. Text: string;
  96. begin
  97. Text := GetLabelText;
  98. if (Flags and DT_CALCRECT <> 0) and ((Text = '') or FShowAccelChar and (Text[1] = '&') and (Text[2] = #0)) then
  99. Text := Text + ' ';
  100. if not FShowAccelChar then Flags := Flags or DT_NOPREFIX;
  101. Flags := DrawTextBiDiModeFlags(Flags);
  102. // 说到底,还是依靠Canvas来画图写文字
  103. Canvas.Font := Font;
  104. if not Enabled then
  105. begin
  106. OffsetRect(Rect, 1, 1); // API
  107. Canvas.Font.Color := clBtnHighlight; // 白亮色
  108. DrawText(Canvas.Handle, PChar(Text), Length(Text), Rect, Flags); // API
  109. OffsetRect(Rect, -1, -1); // API
  110. Canvas.Font.Color := clBtnShadow; // 加阴影
  111. DrawText(Canvas.Handle, PChar(Text), Length(Text), Rect, Flags); // API
  112. end
  113. // 一般走这里
  114. else
  115. DrawText(Canvas.Handle, PChar(Text), Length(Text), Rect, Flags);
  116. end;
  117.  
  118. // SetBounds 做了六件事:重新计算长宽,使控件失效,重新铆接,发消息WM_WINDOWPOSCHANGED通知Windows位置变了,最后对齐,还要调用程序员OnResize事件
  119. procedure TControl.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
  120. begin
  121. if CheckNewSize(AWidth, AHeight) and // TControl的类函数
  122. ((ALeft <> FLeft) or (ATop <> FTop) or (AWidth <> FWidth) or (AHeight <> FHeight)) then
  123. begin
  124. InvalidateControl(Visible, False); // TControl的类函数,第二个参数表示暂时设置当前控件是透明的
  125. FLeft := ALeft;
  126. FTop := ATop;
  127. FWidth := AWidth;
  128. FHeight := AHeight;
  129. UpdateAnchorRules; // TControl的类函数,坐标和长宽设置完了,就要重新铆接一下
  130. // 属性设置完了,如果有API可以使之起作用就当场调用(关于显示部分,不需要句柄就有API使用,这是特殊情况)
  131. Invalidate; // TControl的类函数,调用TControl.InvalidateControl,再调用API声明无效区域
  132. // 此消息在TControl和TWinControl里都有相应的函数,图形控件使用消息再做一些自己力所能及的变化,Win控件使用消息调用类函数使之调用API真正起作用
  133. // 前者重新计算最大化最小化的限制和坞里的尺寸,后者使用API调整边框和控件自己的位置,当然也得重新计算最大化最小化的限制和坞里的尺寸(三明治手法)
  134. Perform(WM_WINDOWPOSCHANGED, 0, 0);
  135. // Windows位置调整完了,还要重新对齐(本质是调用TWinControl.RequestAlign,然后调用API重新排列)
  136. // 但实际上是靠父Win控件重新排列自己,因为它自己没有能力拥有别的控件,当然也就不能实质上让所有控件对齐。
  137. RequestAlign; // TControl的虚函数,各WinControl的子类可自己改写,比如TCustomForm就改写了
  138. if not (csLoading in ComponentState) then Resize; // TControl的虚函数,简单调用程序员事件。子类一般不需要改写它。
  139. end;
  140. end;
  141.  
  142. procedure TControl.WMWindowPosChanged(var Message: TWMWindowPosChanged);
  143. begin
  144. // 先执行潜在的程序员消息函数
  145. // 这里其实不会是TControl自己调用inherited,因为没有TControl的直接实例,而是由它的子类比如TLabel来调用
  146. // 因此会调用TLabel的WM_WINDOWPOSCHANGED消息函数,如果它有的话
  147. inherited;
  148. // 后根据新的长宽,给控件最大长度和最大宽度重新赋值
  149. { Update min/max width/height to actual extents control will allow }
  150. if ComponentState * [csReading, csLoading] = [] then
  151. begin
  152. with Constraints do // 类属性
  153. begin
  154. if (MaxWidth > 0) and (Width > MaxWidth) then
  155. FMaxWidth := Width
  156. else if (MinWidth > 0) and (Width < MinWidth) then
  157. FMinWidth := Width;
  158. if (MaxHeight > 0) and (Height > MaxHeight) then
  159. FMaxHeight := Height
  160. else if (MinHeight > 0) and (Height < MinHeight) then
  161. FMinHeight := Height;
  162. end;
  163. // 根据消息传来的结构体的值,计算坞尺寸
  164. if Message.WindowPos <> nil then
  165. with Message.WindowPos^ do
  166. if (FHostDockSite <> nil) and not (csDocking in ControlState) and
  167. (Flags and SWP_NOSIZE = 0) and (cx <> 0) and (cy <> 0) then
  168. CalcDockSizes; // 类函数
  169. end;
  170. end;

---------------------------------------------------------------

第四步:虽然使用API把文字绘制好了,但是还得等待WM_Paint消息,然后进行绘制。其实图形控件无法直接收到WM_Paint消息,但是其父控件,比如TForm能收到WM_Paint消息,它会检测自己是否有无效区域,然后重绘所有子控件。
因为TForm是直接继承自TWinControl,所以总体顺序如下:
TCustomForm.WMPaint(var Message: TWMPaint);
TWinControl.WMPaint(var Message: TWMPaint);
TWinControl.PaintHandler(var Message: TWMPaint);
TWinControl.PaintWindow(DC: HDC);
TWinControl.PaintControls(DC: HDC; First: TControl);
其中:

  1. procedure TWinControl.PaintControls(DC: HDC; First: TControl);
  2. var
  3. I, Count, SaveIndex: Integer;
  4. FrameBrush: HBRUSH;
  5. begin
  6. if FControls <> nil then // 专指图形控件,不包含windows控件
  7. begin
  8. I := 0;
  9. if First <> nil then
  10. begin
  11. I := FControls.IndexOf(First);
  12. if I < 0 then I := 0;
  13. end;
  14. Count := FControls.Count;
  15. while I < Count do
  16. begin
  17. with TControl(FControls[I]) do
  18. if (Visible or (csDesigning in ComponentState) and not (csNoDesignVisible in ControlStyle)) and
  19. RectVisible(DC, Rect(Left, Top, Left + Width, Top + Height)) then // API,看rect是否在DC中可见
  20. begin
  21. if csPaintCopy in Self.ControlState then Include(FControlState, csPaintCopy);
  22. SaveIndex := SaveDC(DC); // API,重画前,保存父控件的DC
  23. MoveWindowOrg(DC, Left, Top); // 调用2个API
  24. IntersectClipRect(DC, 0, 0, Width, Height); // API,新建一个完全的区域
  25. // 原本图形控件不能直接接受Windows消息的,现在通过VCL体系的变换也接受了。注意传递了父控件的DC
  26. Perform(WM_PAINT, DC, 0); // 图形控件已经把WM_PAINT消息内容已经填好,就等程序员填写Paint函数加上真正要执行的内容。
  27. RestoreDC(DC, SaveIndex); // API,恢复父控件的DC
  28. Exclude(FControlState, csPaintCopy); // 画完之后,去除标记
  29. end;
  30. Inc(I); // 下一个图形控件
  31. end;
  32. end;
  33. end;
  34.  
  35. procedure TGraphicControl.WMPaint(var Message: TWMPaint);
  36. begin
  37. if Message.DC <> 0 then
  38. begin
  39. Canvas.Lock;
  40. try
  41. Canvas.Handle := Message.DC; // DC也是一个Handle。两者的类型都是HDC。important 借用了父类的DC
  42. try
  43. Paint; // 虚函数,直接调用自己的覆盖函数,不用管子控件,这一点与TCustomControl完全不一样。同时它也没有PaintWindow函数
  44. finally
  45. Canvas.Handle := 0; // super,画完了要清零,也许下次WM_Paint消息传来的DC不一致了
  46. end;
  47. finally
  48. Canvas.Unlock;
  49. end;
  50. end;
  51. end;
  52.  
  53. procedure TCustomLabel.Paint;
  54. const
  55. Alignments: array[TAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);
  56. WordWraps: array[Boolean] of Word = (0, DT_WORDBREAK);
  57. var
  58. Rect, CalcRect: TRect;
  59. DrawStyle: Longint;
  60. begin
  61. with Canvas do
  62. begin
  63. if not Transparent then // 类属性
  64. begin
  65. Brush.Color := Self.Color;
  66. Brush.Style := bsSolid;
  67. FillRect(ClientRect); // TCanvas的类函数,TControl的类属性
  68. end;
  69. Brush.Style := bsClear;
  70. Rect := ClientRect; // TControl的类函数,正常情况下就是0,0,Width,Height
  71. { DoDrawText takes care of BiDi alignments }
  72. DrawStyle := DT_EXPANDTABS or WordWraps[FWordWrap] or Alignments[FAlignment];
  73. { Calculate vertical layout }
  74. // 如果是不是顶上的对齐方式,就要重新计算绘制区域
  75. if FLayout <> tlTop then
  76. begin
  77. CalcRect := Rect;
  78. DoDrawText(CalcRect, DrawStyle or DT_CALCRECT); // 增加一个风格,计算需要绘制的区域
  79. if FLayout = tlBottom then // 垂直居下
  80. OffsetRect(Rect, 0, Height - CalcRect.Bottom) // API
  81. else // 垂直居中
  82. OffsetRect(Rect, 0, (Height - CalcRect.Bottom) div 2);
  83. end;
  84. // 根据重新计算过的区域绘制
  85. DoDrawText(Rect, DrawStyle); // super 问题:为什么画两遍?回答:1. 区域有可能被改变 2.此时的绘制风格不包含DT_CALCRECT
  86. end;
  87. end;
  88.  
  89. procedure TCustomLabel.DoDrawText(var Rect: TRect; Flags: Longint);
  90. var
  91. Text: string;
  92. begin
  93. Text := GetLabelText; // 类函数,简单返回Caption字符串
  94. // 计算真正的文字长度(增加一格)
  95. if (Flags and DT_CALCRECT <> 0) and ((Text = '') or FShowAccelChar and (Text[1] = '&') and (Text[2] = #0)) then
  96. Text := Text + ' ';
  97. // 没有前缀,则设置Windows标志位
  98. if not FShowAccelChar then Flags := Flags or DT_NOPREFIX;
  99. Flags := DrawTextBiDiModeFlags(Flags); // TControl的类函数,如有必要颠倒文字的方向标识符
  100. // 说到底,还是依靠Canvas的字体来绘制文字
  101. Canvas.Font := Font; // 将TLabel的Font属性赋值给TLabel内包含的Canvas的字体
  102. if not Enabled then
  103. begin
  104. OffsetRect(Rect, 1, 1); // API
  105. Canvas.Font.Color := clBtnHighlight; // 白亮色
  106. DrawText(Canvas.Handle, PChar(Text), Length(Text), Rect, Flags); // API
  107. OffsetRect(Rect, -1, -1); // API
  108. Canvas.Font.Color := clBtnShadow; // 加阴影
  109. DrawText(Canvas.Handle, PChar(Text), Length(Text), Rect, Flags); // API
  110. end
  111. // 一般走这里
  112. else
  113. DrawText(Canvas.Handle, PChar(Text), Length(Text), Rect, Flags); // API,真正绘制文字!
  114. end;

---------------------------------------------------------------
总结1:
改变TLabel的属性特别简单,纯语言层面赋值即可。但是还要想办法把这个新值绘制到Window窗口上,不管这个窗口是真的Windows控件还是假的Windows控件。这个过程需要发两次消息,第一个消息WM_SETTEXT设置Windows窗口标题(此时TControl冒充了一个Windows句柄窗口,总之Delphi有办法达到这一点),第二个消息CM_TEXTCHANGED根据TLabel事先设置的属性(或者默认的属性)来重新计算文字宽度,上下对齐等等(这中间有些不重要的计算函数没有列出)。最后系统空闲时发现Windows窗口(Form1)有无效区域,于是发WM_PAINT给Form1(因为Label1不是一个实际具有句柄的Windows窗口,它的无效区域算在是Form1窗口上的,所以也代收了WM_Paint消息),才能把Form1.Label1.Caption重绘出效果。

总结2:
另外,绘制TLabel最关键的是TLabel.Paint;函数,可以发现API,即DrawText(Canvas.Handle, PChar(Text), Length(Text), Rect, Flags);使用的句柄是Canvas.Handle。而这个Canvas.Handle是TGraphicControl.WMPaint函数里,由消息传来的父函数的DC句柄,即 Canvas.Handle := Message.DC; 所有图形控件都不用操心这个问题,都由TGraphicControl一手包办了,真不是一般的方便啊。顺便想知道,1995年的时候,那些Borland的神人是怎么设计出这些框架的,是怎么会如此深刻理解OO的(包括它的不足),是怎么深刻理解Windows运行机制并合理安排和使用上千个API,并能做到游刃有余的?真的不可思议。

---------------------------------------------------------------------------------

不过我不明白的是,
TCustomLabel.CMTextChanged函数里调用了Invalidate;和TControl.SetBounds调用了Invalidate;,这不是重复了吗?
TControl.SetBounds里的InvalidateControl(Visible, False);和Invalidate;貌似也重复。
TCustomLabel.AdjustBounds;里调用了DoDrawText和TCustomLabel.Paint;调用了DoDrawText,不是又重复了吗?

还有一个疑问:
procedure TCustomLabel.CMTextChanged(var Message: TMessage);
begin
Invalidate; // 调用TControl.Invalidate;使其图像失效
AdjustBounds; // 类函数,看看有没有必要调整大小和边框
end;
一旦Invalidate;使得部分区域失效以后,会不会WM_Paint抢在AdjustBounds;函数之前工作啊?

一行代码设置TLabel.Caption的前世今生的更多相关文章

  1. 一行代码设置TForm颜色的前世今生(属性赋值引起函数调用,然后发消息实现改变显示效果),TForm的初始颜色在dfm中设置了clBtnFace色

    来自万一的帖子:http://www.cnblogs.com/del/archive/2008/04/27/1173658.html的确做到了一行代码设置TForm控件的颜色(一点感想:Delphi程 ...

  2. 一行代码设置UITableView分割线的长度

    使用UITableView时会发现分割线的长度是这样的: 而QQ里面分割线左端到昵称的下面就截止了: 只需行代码就可以搞定: self.tableView.separatorInset = UIEdg ...

  3. 使用StoryBoard设置Scrollview的横向滚动不用一行代码

    1).创建一个空工程Single类型的工程,然后打开故事版(StoryBoard)在ViewController上添加scrollview 2).然后对scrollview添加约束,上下左右全部都是0 ...

  4. eclipse快速复制一行代码(向下/向上)快捷键修改设置

    eclipse快速复制一行代码(向下/向上)快捷键修改设置 2015年10月05日 17:46:57 xiaoguanglgc 阅读数:20906 标签: eclipse快速复制一行快捷键冲突英特尔 ...

  5. [WCF]缺少一行代码引发的血案

    这是今天作项目支持的发现的一个关于WCF的问题,虽然最终我只是添加了一行代码就解决了这个问题,但是整个纠错过程是痛苦的,甚至最终发现这个问题都具有偶然性.具体来说,这是一个关于如何自动为服务接口(契约 ...

  6. Android之ListView性能优化——一行代码绑定数据——万能适配器

    如下图,加入现在有一个这样的需求图,你会怎么做?作为一个初学者,之前我都是直接用SimpleAdapter结合一个Item的布局来实现的,感觉这样实现起来很方便(基本上一行代码就可以实现),而且也没有 ...

  7. 一行代码,让你的应用中UIScrollView的滑动与侧滑返回并存

    侧滑返回是iOS系统的一个很贴心的功能,特别是在大屏手机上,单手操作的时候去按左上角的返回键特别不方便.当我在使用一个APP的时候,如果控制器不能侧滑返回,我会觉得这个APP十分不友好...这款产品在 ...

  8. iOS 3DES加密解密(一行代码搞定)

    3DES(或称为Triple DES)是三重数据加密算法(TDEA,Triple Data Encryption Algorithm)块密码的通称.它相当于是对每个数据块应用三次DES加密算法.由于计 ...

  9. Winform开发框架之权限管理系统改进的经验总结(4)-一行代码实现表操作日志记录

    在前面介绍了几篇关于我的权限系统改进的一些经验总结,本篇继续这一系列主体,介绍如何一行代码实现重要表的操作日志记录.我们知道,在很多业务系统里面,数据是很敏感的,特别对于一些增加.修改.删除等关键的操 ...

随机推荐

  1. python关于字典的使用方法

    #-*- coding:utf-8 -*-#Author:gxli#定义字典id_db={ 233333199211222342:{ 'name':'xiaoa', 'age':23, 'addr': ...

  2. 微信小程序购物商城系统开发系列

    微信小程序购物商城系统开发系列 微信小程序开放公测以来,一夜之间在各种技术社区中就火起来啦.对于它 估计大家都不陌生了,对于它未来的价值就不再赘述,简单一句话:可以把小程序简单理解为一个新的操作系统. ...

  3. 测试web数据库的分布式事务atomikos 的三种数据源 SimpleDataSourceBean,AtomikosDataSourceBean,AtomikosNonXADataSourceBean

    这2天学习了atomikos事务控制框架,其中看到有3种数据源,分别是,SimpleDataSourceBean,AtomikosDataSourceBean,AtomikosNonXADataSou ...

  4. 四则运算出题器(c++)

    一.设计思路 这次版本加入了一下功能: 可定制题目的数量:修改循环次数: 可以定制每行打印的题目数和行间距的大小(当前题目序号可以整除定制数时输出输入的行间距个换行符): 可以定制算式的范围(修改随机 ...

  5. Mac系统如何配置adb

    在使用mac进行android开发之前,我们一般会安装android studio 或者 eclipse,无论哪一款开发软件,都少不了安装adb(Android Debug Bridge).adb(A ...

  6. js判断浏览器滚动条是否拉到底

    $(window).scroll(function(){ // 当滚动到最底部以上n像素时, 加载新内容 if ($(document).height() - $(this).scrollTop() ...

  7. 设计模式之外观模式(Facade)

    外观模式原理:将复杂的子系统的结构封装起来,只提供客户一个简单的接口 代码如下: #include <iostream> #include <string> #include ...

  8. iOS开发如何实现消息推送机制

    一.关于推送通知 推送通知,也被叫做远程通知,是在iOS 3.0以后被引入的功能.是当程序没有启动或不在前台运行时,告诉用户有新消息的一种途径,是从外部服务器发送到应用程序上的.一般说来,当要显示消息 ...

  9. 操作集合的工具类Collections

    1       操作集合的工具类Collections Java提供了一个操作Set.List和Map等集合的工具类:Collections,该工具类里提供了大量方法对集合元素进行排序.查询和修改等操 ...

  10. HDU 1104 Remainder

    与前一题类似,也是BFS+记录路径, 但是有很多BUG点, 第一MOD操作与%不同i,其实我做的时候注意到了我们可以这样做(N%K+K)%K就可以化为正数,但是有一点要注意 N%K%M!=N%M%K; ...