示例:图形用户界面工具箱 说明: 许多图形用户界面工具箱将用户应用的界面表示与底下的应用数据分离。定义应用数据的类和负责界面表示的类可以各自独立地复用。当然它们也可一起工作。一个表格对象和一个柱状图对象可使用不同的表示形式描述同一个应用数据对象的信息。表格对象和柱状图对象互相并不知道对方的存在,这样使你可以根据需要单独复用表格或柱状图。但在这里是它们表现的似乎互相知道。当用户改变表格中的信息时,柱状图能立即反映这一变化,反过来也是如此。 这一行为意味着表格对象和棒状图对象都依赖于数据对象,因此数据对象的任何状态改变都应立即通知它们。同时也没有理由将依赖于该数据对象的对象的数目限定为两个,对相同的数据可以有任意数目的不同用户界面。 Observer模式描述了如何建立这种关系。 界面: object Form3: TForm3 Left = 192 Top = 110 Width = 371 Height = 294 Caption = 'Form3' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False OnCreate = FormCreate OnDestroy = FormDestroy PixelsPerInch = 96 TextHeight = 13 object Button1: TButton Left = 56 Top = 224 Width = 75 Height = 25 Caption = 'Button1' TabOrder = 0 OnClick = Button1Click end object Button2: TButton Left = 152 Top = 224 Width = 75 Height = 25 Caption = 'Button2' TabOrder = 1 OnClick = Button2Click end end 代码:
unit uView; interface uses Windows, SysUtils, Classes, Graphics, Controls, Math; type TDataModel = class; TView = class(TGraphicControl) public procedure Update1; virtual; abstract; end; TDataView = class(TView) private FModel: TDataModel; public constructor Create(AOwner: TComponent; AModel: TDataModel); reintroduce; destructor Destroy; override; //--- procedure Update1; override; end; TTableView = class(TDataView) protected procedure Paint; override; end; TStickView = class(TDataView) protected procedure Paint; override; end; TPieView = class(TDataView) protected procedure Paint; override; end; TModel = class private FObservers: TList; public constructor Create; destructor Destroy; override; //--- procedure Attach(AObserver: TView); procedure Detach(AObserver: TView); procedure Notify(); end; RData = record x, y, z: Integer; end; TDataModel = class(TModel) private FA: RData; FB: RData; FC: RData; public property A: RData read FA write FA; property B: RData read FB write FB; property C: RData read FC write FC; end; function GetData(AX, AY, AZ: Integer): RData; implementation function GetData(AX, AY, AZ: Integer): RData; begin with Result do begin x := AX; y := AY; z := AZ; end; end; constructor TModel.Create; begin inherited; //--- FObservers := TList.Create; end; destructor TModel.Destroy; begin FObservers.Free; //--- inherited; end; procedure TModel.Attach(AObserver: TView); begin FObservers.Add(AObserver); end; procedure TModel.Detach(AObserver: TView); begin FObservers.Remove(AObserver); end; procedure TModel.Notify(); var i: Integer; begin with FObservers do begin for i := 0 to Count - 1 do TView(Items[i]).Update1; end; end; procedure TTableView.Paint; //--- procedure _ClearRect; begin with self.Canvas do begin with Brush do begin Color := clBlack; Style := bsSolid; end; FillRect(self.ClientRect); end; end; //--- procedure _DrawTable; const CNT_RowCount = 4; CNT_ColCount = 4; var ARect: TRect; ARowHeight, AColWidth: Integer; //--- procedure _GetRect; const CNT_Size = 5; begin ARect := self.ClientRect; with ARect do begin Left := Left + CNT_Size; Right := Right - CNT_Size; Top := Top + CNT_Size; Bottom := Bottom - CNT_Size; end; end; //--- procedure _GetAxis; begin with ARect do begin AColWidth := (Right - Left) div CNT_ColCount; ARowHeight := (Bottom - Top) div CNT_RowCount; end; end; //--- function _GetX(ACol: Integer): Integer; begin with ARect do Result := Left + AColWidth * ACol; end; //--- function _GetY(ARow: Integer): Integer; begin with ARect do Result := Top + ARowHeight * ARow; end; //--- procedure _DrawGrid; var i, X, Y: Integer; begin with self.Canvas do begin with Pen do begin Color := clYellow; Width := 1; Style := psSolid; end; Rectangle(ARect); //--- with ARect do begin for i := 1 to CNT_RowCount - 1 do begin Y := _GetY(i); MoveTo(Left, Y); LineTo(Right, Y); end; //--- for i := 1 to CNT_ColCount - 1 do begin X := _GetX(i); MoveTo(X, Top); LineTo(X, Bottom); end; end; end; end; //--- procedure _DrawData; //--- procedure _DrawColDatas(ACol: Integer; const ADatas: array of string); var X, ARow: Integer; begin X := _GetX(ACol); with self.Canvas do begin for ARow := Low(ADatas) to High(ADatas) do TextOut(X + 2, _GetY(ARow) + 2, ADatas[ARow]); end; end; begin with self.Canvas do begin with Font do begin Color := clYellow; Size := 10; end; end; //--- with FModel do begin _DrawColDatas(0, ['', 'x', 'y', 'z']); with A do _DrawColDatas(1, ['a', IntToStr(x), IntToStr(y), IntToStr(z)]); with B do _DrawColDatas(2, ['b', IntToStr(x), IntToStr(y), IntToStr(z)]); with C do _DrawColDatas(3, ['c', IntToStr(x), IntToStr(y), IntToStr(z)]); end; end; begin _GetRect; _GetAxis; _DrawGrid; _DrawData; end; begin _ClearRect; _DrawTable; end; constructor TDataView.Create(AOwner: TComponent; AModel: TDataModel); begin inherited Create(AOwner); //--- FModel := AModel; FModel.Attach(self); end; destructor TDataView.Destroy; begin FModel.Detach(self); //--- inherited; end; procedure TDataView.Update1; begin self.Paint; end; procedure TStickView.Paint; //--- procedure _ClearRect; begin with self.Canvas do begin with Brush do begin Color := clBlack; Style := bsSolid; end; FillRect(self.ClientRect); end; end; //--- procedure _DrawView; const CNT_ColCount = 3; var ARect: TRect; AValueHeight, AColWidth: Integer; //--- procedure _GetRect; const CNT_Size = 5; begin ARect := self.ClientRect; with ARect do begin Left := Left + CNT_Size; Right := Right - CNT_Size; Top := Top + CNT_Size; Bottom := Bottom - CNT_Size * 4; end; end; //--- procedure _GetAxis; var AMaxValue: Integer; begin with ARect do begin AColWidth := (Right - Left) div CNT_ColCount; //--- with FModel do begin AMaxValue := A.y; with B do if AMaxValue < y then AMaxValue := y; with C do if AMaxValue < y then AMaxValue := y; AMaxValue := Trunc(AMaxValue * 1.2); end; //--- if AMaxValue > 0 then AValueHeight := (Bottom - Top) div AMaxValue else AValueHeight := (Bottom - Top); end; end; //--- function _GetX(ACol: Integer): Integer; begin with ARect do Result := Left + trunc(AColWidth * (ACol + 0.5)); end; //--- function _GetY(AValue: Integer): Integer; begin with ARect do Result := Bottom - AValueHeight * AValue; end; //--- procedure _DrawAxis; begin with self.Canvas do begin with Pen do begin Color := clYellow; Width := 1; Style := psSolid; end; //--- with ARect do begin MoveTo(Left, Bottom); LineTo(Right, Bottom); //--- MoveTo(Left, Top); LineTo(Left, Bottom); end; end; end; //--- procedure _DrawData; //--- procedure _DrawColData(ACol: Integer; const ALable: string; const AValue: Integer); var X, AWidth: Integer; begin X := _GetX(ACol); with self.Canvas do begin Brush.Style := bsClear; TextOut(X, ARect.Bottom + 2, ALable); //--- if AValue > 0 then begin with Brush do begin Color := clYellow; Style := bsSolid; end; //--- AWidth := AColWidth div 4; Rectangle(X - AWidth, _GetY(AValue), X + AWidth, ARect.Bottom); end; end; end; begin with self.Canvas do begin with Font do begin Color := clYellow; Size := 10; end; end; //--- with FModel do begin _DrawColData(0, 'a', A.y); _DrawColData(1, 'b', B.y); _DrawColData(2, 'c', C.y); end; end; begin _GetRect; _GetAxis; _DrawAxis; _DrawData; end; begin _ClearRect; _DrawView; end; procedure TPieView.Paint; //--- procedure _ClearRect; begin with self.Canvas do begin with Brush do begin Color := clBlack; Style := bsSolid; end; FillRect(self.ClientRect); end; end; //--- procedure _DrawView; var ARect: TRect; R, SumValue: Integer; P: TPoint; //--- procedure _GetRect; const CNT_Size = 5; begin ARect := self.ClientRect; with ARect do begin Left := Left + CNT_Size; Right := Right - CNT_Size; Top := Top + CNT_Size; Bottom := Bottom - CNT_Size; end; end; //--- procedure _GetAxis; begin with ARect do begin R := Min(Right - Left, Bottom - Top) div 2; P.X := (Left + Right) div 2; P.Y := (Top + Bottom) div 2; end; //--- with FModel do SumValue := A.y + B.y + C.y; end; //--- function _GetRadian(AValue: Integer): double; begin Result := 2 * Pi * (AValue / SumValue); end; //--- function _GetX(AValue, ARadius: Integer): Integer; begin Result := P.X + trunc(ARadius * cos(_GetRadian(AValue))); end; //--- function _GetY(AValue, ARadius: Integer): Integer; begin Result := P.Y - trunc(ARadius * sin(_GetRadian(AValue))); end; //--- procedure _DrawAxis(const AValues: array of Integer); var X, Y, AValue, i: Integer; begin with self.Canvas do begin with Pen do begin Color := clYellow; Width := 1; Style := psSolid; end; //--- Ellipse(P.X - R, P.Y - R, P.X + R, P.Y + R); //--- MoveTo(P.X, P.Y); LineTo(P.X + R, P.Y); //--- AValue := 0; for i := Low(AValues) to High(AValues) do begin AValue := AValue + AValues[i]; //--- X := _GetX(AValue, R); Y := _GetY(AValue, R); //--- MoveTo(P.X, P.Y); LineTo(X, Y); end; end; end; //--- procedure _DrawData(const AValues: array of Integer; const ALables: array of string); var X, Y, AValue, ASumValue, ARadius, i: Integer; begin with self.Canvas do begin with Font do begin Color := clYellow; Size := 10; end; Brush.Style := bsClear; //--- ARadius := R div 2; ASumValue := 0; for i := Low(AValues) to High(AValues) do begin AValue := ASumValue + AValues[i] div 2; ASumValue := ASumValue + AValues[i]; //--- X := _GetX(AValue, ARadius); Y := _GetY(AValue, ARadius); //--- with TextExtent(ALables[i]) do TextOut(X - cX div 2, Y - cY div 2, ALables[i]); end; end; end; begin _GetRect; _GetAxis; with FModel do begin if SumValue > 0 then begin _DrawAxis([A.y, B.y]); _DrawData([A.y, B.y, C.y], ['a', 'b', 'c']); end; end; end; begin _ClearRect; _DrawView; end; end. unit Unit3; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, uView; type TForm3 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormCreate(Sender: TObject); private FModel: TDataModel; FTableView: TTableView; FStickView: TStickView; FPieView:TPieView; public { Public declarations } end; var Form3: TForm3; implementation {$R *.dfm} procedure TForm3.FormCreate(Sender: TObject); begin FModel := TDataModel.Create; //--- FTableView := TTableView.Create(nil, FModel); with FTableView do begin Parent := self; Top := 10; Left := 10; Width := 100; Height := 100; end; //--- FStickView := TStickView.Create(nil, FModel); with FStickView do begin Parent := self; Top := 10; Left := 120; Width := 100; Height := 100; end; //--- FPieView := TPieView.Create(nil, FModel); with FPieView do begin Parent := self; Top := 10; Left := 230; Width := 100; Height := 100; end; end; procedure TForm3.FormDestroy(Sender: TObject); begin FTableView.Free; FStickView.Free; FPieView.Free; FModel.Free; end; procedure TForm3.Button1Click(Sender: TObject); begin with FModel do begin A := GetData(60, 50, 80); B := GetData(30, 30, 10); C := GetData(10, 20, 10); //--- Notify; end; end; procedure TForm3.Button2Click(Sender: TObject); begin with FModel do begin A := GetData(60, 20, 80); B := GetData(30, 40, 10); C := GetData(10, 40, 10); //--- Notify; end; end; end.