| 
				 四、答题卡组件工具条
      这个工具条上的按钮是设计器所需要的答题卡组件,如图2所示。这个工具条的窗体名为frmTool,窗体类型为fsStayOnTop。在点击每一个组件按钮时,系统都会记录所点的是哪个组件。每一个组件按钮是一个TSpeedButton。在TspeedButton的tag中保存了当前的组件按钮代表哪一个答题卡组件。所有的组件按钮都执行同一段代码。这段代码如下: procedure TfrmTool.SpeedButton1Click(Sender: TObject); 
var 
  widget_id: integer; 
begin 
  ButtonDown(FunGroup1, Sender);  // 如果某个按钮被按下,将这个按钮设为按下状态 
  if drawing_widget <> nil then   
  begin 
    drawing_widget.Free; 
    drawing_widget := nil 
  end; 
  widget_id := (Sender as TSpeedButton).Tag;  // 获得当前组件类型 
  if widget_id = 0 then   // 设置当前的鼠标指针 
    frmMain.Card.Cursor := crDefault 
  else 
    frmMain.Card.Cursor := crCross; 
  case widget_id of   // drawing_widget为全局变量,保存了当前被选中的答题卡组件 
    VERTICAL_TBT:  // 选中纵向同步头 
    begin 
      drawing_widget := TTBT_Vertical.Create(nil) as TWidgetContainer; 
    end; 
    HORIZONTAL_TBT:  // 选中横向同步头 
    begin 
      drawing_widget := TTBT_Horizontal.Create(nil) as TWidgetContainer; 
    end; 
    INFOCOLLECTION:   // 选中信息采集区 
    begin 
      drawing_widget := TInfoCollection .Create(nil) as TWidgetContainer; 
    end; 
    QUESTIONREGION:   // 选中答题区 
    begin 
      drawing_widget := TQuestionRegion .Create(nil) as TWidgetContainer; 
    end; 
    ZKZREGION:   // 选中准考证 
    begin 
      drawing_widget := TZKZ .Create(nil) as TWidgetContainer; 
    end; 
    BORDER:   // 选中边框 
    begin 
      drawing_widget := TBorder .Create(nil) as TWidgetContainer; 
    end; 
    BORDEREXTENSION:   // 选中扩展边框 
    begin 
      drawing_widget := TBorderExtension .Create(nil) as TWidgetContainer; 
    end; 
    VERTICAL1_TBT:   // 选中只有两个方框的纵向同步头 
    begin 
      drawing_widget :=  TTBT_Vertical1.Create(nil) as TWidgetContainer; 
    end; 
    HORIZONTAL1_TBT:  // 选中只有两个方框的横向同步头 
    begin 
      drawing_widget :=  TTBT_Horizontal1.Create(nil) as TWidgetContainer; 
    end; 
    VERTICAL_LINE:   // 选中纵向非答题区 
    begin 
      drawing_widget :=  TVertical_Line.Create(nil) as TWidgetContainer; 
    end; 
    VERTICAL_SINGLELINE:  // 选中纵向直线 
    begin 
      drawing_widget :=  TVertical_SingleLine.Create(nil) as TWidgetContainer; 
    end; 
    HORIZONTAL_SINGLELINE:  // 选中横向直线 
    begin 
      drawing_widget :=  THorizontal_SingleLine.Create(nil) as TWidgetContainer; 
    end; 
    HORIZONTAL_LINE:   // 选中横向非答题区 
    begin 
      drawing_widget :=  THorizontal_Line.Create(nil) as TWidgetContainer; 
    end; 
  end; 
end; 
五、主界面
      在设计中将答题卡画在TPaintBox上。在画完组件,鼠标抬起后。将在MouseUp事件中把drawing_widget所指的组件对象放到TPaintBox上。MouseUp事件的代码如下:
 
  
procedure TfrmMain.pbCardMouseUp(Sender: TObject; Button: TMouseButton; 
  Shift: TShiftState; X, Y: Integer); 
var 
  i: integer; 
  function Abs_Int(x: integer): integer;   // 取整数的绝对值 
  begin 
    if x < 0 then 
      result := -x 
    else 
      result := x; 
  end; 
  function IsSelected(control: TControl): boolean;   // 判断组件是否应被选中 
  var 
    NewX, NewY, h, w: integer; 
  begin 
    if X < OldX then  // 鼠标向左侧画 
    begin 
      NewX := X; 
      w := OldX - X; 
    end 
    else   // 鼠标向右侧画 
    begin 
      NewX := OldX; 
      w := X - OldX; 
    end; 
    if Y < OldY then  // 鼠标向上侧画 
    begin 
      NewY := Y; 
      h := OldY - Y; 
    end 
    else  // 鼠标向下侧画 
    begin 
      NewY := OldY; 
      h := Y - OldY; 
end; 
// 判断鼠标所画的区域是否包含了control所指的组件对象 
    if (control.Left + control.Width >= NewX) and (control.Top + control.Height >= NewY) then 
    begin 
      if (NewX + w > control.Left) and (NewY + h > control.Top) then 
      begin 
        result := true; 
        exit; 
      end; 
    end; 
    result := false; 
  end; 
  
begin 
  drawflag := false; 
  pbCard.Repaint;  // 将画布重画 
  for i := 0 to Card.ControlCount - 1 do  // 根据鼠标画的区域选择答题卡的所有的组件 
  begin 
    if Card.Controls[i] is TWidgetContainer then 
    begin 
      if IsSelected(Card.Controls[i]) then 
        (Card.Controls[i] as TWidgetContainer).WidgetStatus := [wsAll]; 
    end; 
  end; 
  if drawing_widget <> nil then  // 开始画答题卡组件 
  begin 
    drawing_widget.Parent := card;  // 将组件放到TPaintBox中 
    drawing_widget.Width := Abs_Int(X - OldX);  // 设置组件的宽度 
    if X < OldX then  // 计算控制是向左侧画,还是向右侧画 
      drawing_widget.Left := X 
    else 
      drawing_widget.Left := OldX; 
  
    if Y < OldY then  // 计算控制是向上画,还是向下画 
      drawing_widget.Top := Y 
    else 
      drawing_widget.Top := OldY; 
  
    drawing_widget.Height := Abs_Int(Y - OldY);  // 设置组件的高度 
    drawing_widget.WidgetStatus := [wsAll];   // 将组件设为被选中状态(显示所有的尺寸块) 
  
    current_widget := drawing_widget; 
    drawing_widget := nil; 
    frmTool.SpeedButton1Click(frmTool.SpeedButton1); 
    changed;    // 通知系统当前的答题卡已经被改变 
  end; 
end;  
答题卡设计器不仅可以在TPaintBox上设计答题卡,而且还可以根据所设计的答题卡生成一个bmp图。生成bmp图的代码如下: procedure TfrmMain.SaveCard(fn: string); 
var 
  bmp: TBitmap; 
  i: integer; 
  rect, rect1: TRect; 
  wc: TWidgetContainer; 
  OldCanvas: TCanvas; 
begin 
  bmp := TBitmap.Create;  // 建立一个Tbitmap对象 
  bmp.Width := card.Width;  // 将这个Tbitmap对象的宽度设为TPaintBox的宽度 
  bmp.Height := card.Height;  // 将这个Tbitmap对象的高度设为TPaintBox的高度 
  for i := 0 to card.ControlCount - 1 do   // 扫描打题卡中的所有组件,将它们依次画到bmp图上 
  begin 
    if card.Controls[i] is TWidgetContainer then  // 当TPaintBox中的控制是TwidgetContainer时进行 
    begin 
      wc := card.Controls[i] as TWidgetContainer;   
      rect.Left := wc.Left; 
      rect.Top := wc.Top; 
      rect.Right := wc.Width; 
      rect.Bottom := wc.Height; 
      rect1.Left := 0; 
      rect1.Top := 0;                    
      rect1.Right := wc.Width; 
      rect1.Bottom := wc.Height; 
      wc.DrawWidgetToCanvas(bmp.Canvas);  // 开始在bmp上画相应的答题卡组件 
    end; 
  end; 
  bmp.SaveToFile(fn);   // 保存bmp图 
  bmp.Free; 
end; 
 六、总结 
在本文中给出了实现答题卡设计器的核心代码。感兴趣的读者可以根据这些源代码来增加程序的功能,从而设计出更为丰富的答题卡设计器来。 			
				 |