高分求救:关于编写‘撤消’操作的问题 (100分)

  • 主题发起人 主题发起人 liguowei
  • 开始时间 开始时间
L

liguowei

Unregistered / Unconfirmed
GUEST, unregistred user!
我想在form的右上角放一个按钮,需要是单击该按钮就可撤消一步以前的操作,单击几次则撤消几次,就是undo事件。
 
这个要分具体问题来看,说清楚点
 
看是在什么情况下的,比如:编辑文本、操作软件等,不同的情况用不同的方法
 
比较复杂:(
 
两层: TDataSet.Cancel
三层: 重新查询
 
你要自己写程序,
来记录你每次修改的变化,
来完成这个。
 
你上一步的操作是什么?
 
转贴一个...

通过状态方式完成撤消-重做功能。

据我所知,存在两种撤消-重做的方法。第一种是将当前系统的状态在它被修改前保存到一个列表中。在你的编辑器中可能会有 GetState 和 SetState 方法。第二种方法即是保存命令,每个命令都可以被撤消和重做。
当你的编辑器有许多功能但编辑的数据比较少如10-20K时,采用保存状态的方式比较好,更简捷。当你编辑一个图像时,你可能将撤消和重做的信息保存在一个文件中,这时最好采用向量图的方式,因为这样更节约空间。
当你编辑大量数据时,采用保存命令的复杂方式就有必要了,尽管这样对编码要求更高,因为如果用保存状态的方式就会消耗大量的时间。
我自己编写了一个撤消-重做状态的类,主类(TUndoRedoState)中保持状态的快照,在"IState"接口中有两个方法:GetState和SetState。我在编辑器的实现代码中实现了这个接口。
给主类的构造函数中传送IState接口。调用撤消和重做将调用GetState和SetState函数。如果你不喜欢使用接口,也可以采用方法指针来修改代码以获取GetState和SetState方法。
用法示例:
TMyForm = class(TForm, IState)
procedure FormCreate(Sender: TObject);
private
FUndoRedo: TUndoRedoState;
procedure GetState(S: TStream);
procedure SetState(S: TStream);
end;

=====
procedure TMyForm.FormCreate(Sender: TObject);
begin
FUndoRedo:= TUndoRedoState.Create(Self);
end;

....
然后你就可以在你的代码中调用 FUndoRedo.BeginModify 和 EndModify;

你也可以添加一对方法如 CanUndo 和 CanRedo 以便决定是否允许撤消、重做按钮.


Happy coding!

Download the source from the url.

Full Source:
2 units:

===============================
unit _State;

interface
uses
Classes;

type
IState = interface
procedure GetState(S: TStream);
procedure SetState(S: TStream);
end;

implementation

end.
============================
unit UndoRedoState;

interface
uses
_State, Classes, SysUtils;

// A value of 0 for MaxMemoryUsage means unlimited (default).
type
TUndoRedoState = class
private
FState: IState;
FUndoRedoList: TList;
FModifyCount: Integer;
FUndoPos: Integer;
FTailState: TStream;
FMaxMemoryUsage: LongWord;
FCurrMemUsage: LongWord;
function CreateCurrentState: TStream;
procedure SetMaxMemoryUsage(const Value: LongWord);
procedure TruncToMem;
public
constructor Create(AState: IState);
property MaxMemoryUsage: LongWord read FMaxMemoryUsage write SetMaxMemoryUsage;
procedure BeginModify;
procedure EndModify;
procedure Undo;
procedure Redo;
destructor Destroy; override;
end;

implementation

{ TUndoRedoState }

procedure TUndoRedoState.BeginModify;
var
I: Integer;
S: TStream;
begin
Inc(FModifyCount);
if FModifyCount = 1 then
begin
for I:= FUndoRedoList.Count-1 downto FUndoPos+1 do
begin
S:= FUndoRedoList;
Dec(FCurrMemUsage, S.Size);
FUndoRedoList.Delete(I);
S.Free;
end;
S:= CreateCurrentState;
Inc(FCurrMemUsage, S.Size);
FUndoRedoList.Add(S);
FUndoPos:= FUndoRedoList.Count-1;
if FTailState <> nil then
begin
Dec(FCurrMemUsage, FTailState.Size);
FreeAndNil(FTailState);
end;
TruncToMem;
end;
end;

constructor TUndoRedoState.Create(AState: IState);
begin
Assert(AState <> nil, 'AState should not be nil for '
+'"TUndoRedoState.Create(AState: IState)"');

inherited Create;
FState:= AState;
FUndoRedoList:= TList.Create;
FUndoPos:= -1;
end;

function TUndoRedoState.CreateCurrentState: TStream;
begin
Result:= TMemoryStream.Create;
try
FState.GetState(Result);
except
Result.Free;
raise;
end;
end;

destructor TUndoRedoState.Destroy;
var
I: Integer;
begin
FState:= nil;
for I:= 0 to FUndoRedoList.Count-1 do
TObject(FUndoRedoList).Free;

FTailState.Free;

inherited Destroy;
end;

procedure TUndoRedoState.EndModify;
begin
Assert(FModifyCount > 0, 'TUndoRedoState.EndModify: EndModify was called '
+'more times than BeginModify');

Dec(FModifyCount);
end;

procedure TUndoRedoState.Redo;
var
FRedoPos: Integer;
begin
Assert(FModifyCount=0, 'TUndoRedoState.Redo: should not be called while '
+'modifying');

if (FUndoRedoList.Count > 0) and (FUndoPos < (FUndoRedoList.Count-1)) then
begin
FRedoPos:= FUndoPos+2;
if FRedoPos > (FUndoRedoList.Count-1) then
begin
FState.SetState(FTailState);
Dec(FCurrMemUsage, FTailState.Size);
FreeAndNil(FTailState);
end
else
FState.SetState(FUndoRedoList[FRedoPos]);
Inc(FUndoPos);
end;
end;

procedure TUndoRedoState.SetMaxMemoryUsage(const Value: LongWord);
begin
FMaxMemoryUsage := Value;
end;

procedure TUndoRedoState.TruncToMem;
var
S: TStream;
begin
if (FMaxMemoryUsage > 0) and (FCurrMemUsage > FMaxMemoryUsage) then
begin
while (FUndoRedoList.Count > 0) and (FCurrMemUsage > FMaxMemoryUsage) do
begin
S:= FUndoRedoList[0];
FUndoRedoList.Delete(0);
Dec(FCurrMemUsage, S.Size);
Dec(FUndoPos);
end;

if (FUndoRedoList.Count = 0) and (FCurrMemUsage > FMaxMemoryUsage) then
if FTailState <> nil then
begin
Dec(FCurrMemUsage, FTailState.Size);
FreeAndNil(FTailState);
end;
end;
end;

procedure TUndoRedoState.Undo;
var
S: TStream;
begin
Assert(FModifyCount=0, 'TUndoRedoState.Undo: should not be called while '
+'modifying');

if FUndoPos >= 0 then
begin
if FUndoPos = (FUndoRedoList.Count-1) then
begin
FTailState:= CreateCurrentState;
Inc(FCurrMemUsage, FTailState.Size);
end;
S:= FUndoRedoList[FUndoPos];
Dec(FUndoPos);
FState.SetState(S);
TruncToMem;
end;
end;

end.

 
主要是我修改了dbgrid中的记录:1,随意删除一条记录
2,随意修改了任意一条记录
3,随意添加了一条记录
如果我随意操作了[red]若干步[/red]或1,或2,或3,我想撤消这些操作,我应如何?。
 
自己用文件按顺序记录操作事件
 
先记住这些,然后,做反操作不就成了.
 
有一些难
到此为止
请来拿分
 
拿分了!
 

Similar threads

D
回复
0
查看
937
DelphiTeacher的专栏
D
D
回复
0
查看
892
DelphiTeacher的专栏
D
D
回复
0
查看
864
DelphiTeacher的专栏
D
后退
顶部