Как создать список последних использованных файлов в Delphi 2009? - PullRequest
3 голосов
/ 05 января 2011

У меня есть TActionManager и TActionMainMenuBar, и я знаю, как добавить TActionClientItem для каждого файла MRU в строку главного меню. Но нужно ли создавать отдельное действие для каждого файла MRU в списке? Или есть способ создать только одно действие и каким-либо образом передать тег или что-то в событие OnExecute действия, в зависимости от того, какой файл MRU был нажат?

Справка Delphi гласит: «Для получения дополнительной информации о списках MRU, образце кода и методах поиска действий в списках см. FindItemByAction и FindItemByCaption в онлайн-справке». Но я не могу найти в этих предметах ничего полезного и, конечно, не пример кода. Я действительно хотел бы избежать использования стороннего компонента, чтобы сделать это.

Ответы [ 2 ]

4 голосов
/ 05 января 2011

Я использую код следующим образом, но вам, возможно, придется обойти его. Единственное, чего явно не хватает - это IAbbreviatedFileName, который, по сути, оборачивает функцию Windows API PathCompactPath. Вы захотите каким-то образом сократить очень длинные имена файлов, и это мой предпочтительный выбор. Извините за такой огромный дамп кода, но кто-то может найти что-то полезное внутри!

type
  TFileAwareMenuItem = class(TMenuItem)
  private
    FFileName: string;
  public
    property FileName: string read FFileName write FFilename;
  end;

  TMRU = class
  private
    FParent: array of TMenuItem;
    FMenuItemStart: array of TMenuItem;
    FMenuItemFinish: array of TMenuItem;
    FMenuCount: Integer;
    FRegistryKey: string;
    FOwner: TCustomForm;
    FMRUFileNames: TStringList;
    FAction: TAction;
    function GetCount: Integer;
    function GetItem(Index: Integer): string;
    procedure SetAction(Value: TAction);
    procedure Read;
    procedure Write;
    procedure UpdateMenu;
  public
    constructor Create(const RegistrySubKey: string; const Owner: TCustomForm);
    destructor Destroy; override;
    procedure RegisterBoundingMenuItems(Start, Finish: TMenuItem);
    procedure Add(const FileName: string);
    procedure Delete(ItemNum: Integer);
    property Count: Integer read GetCount;
    property Action: TAction read FAction write SetAction;
    property Items[Index: Integer]: string read GetItem; default;
  end;

const
  MRUSize=9;
  AppRegistryKey='??put your apps registry key here??';

var
  Registry: TRegistry;

constructor TMRU.Create(const RegistrySubKey: string; const Owner: TCustomForm);
begin
  inherited Create;
  FRegistryKey := Format('%s\%s', [AppRegistryKey, RegistrySubKey]);
  FOwner := Owner;
  FMRUFileNames := TStringList.Create;
  Read;
end;

destructor TMRU.Destroy;
begin
  Write;
  FreeAndNil(FMRUFileNames);
  inherited;
end;

procedure TMRU.RegisterBoundingMenuItems(Start, Finish: TMenuItem);
begin
  inc(FMenuCount);
  SetLength(FParent, FMenuCount);
  SetLength(FMenuItemStart, FMenuCount);
  SetLength(FMenuItemFinish, FMenuCount);

  FMenuItemStart[FMenuCount-1] := Start;
  FMenuItemFinish[FMenuCount-1] := Finish;
  Assert(Start.Parent=Finish.Parent);
  FParent[FMenuCount-1] := Start.Parent;

  UpdateMenu;
end;

procedure TMRU.UpdateMenu;
var
  Intf: IAbbreviatedFileName;
  i, j: Integer;
  FileName: string;
  NewMenuItem: TFileAwareMenuItem;
begin
  Intf := FOwner as IAbbreviatedFileName;
  for i := 0 to FMenuCount-1 do begin
    j := FMenuItemStart[i].MenuIndex+1;
    while j<FMenuItemFinish[i].MenuIndex do begin
      FParent[i][j].Free;
    end;
    for j := 0 to Count-1 do begin
      NewMenuItem := TFileAwareMenuItem.Create(FMenuItemStart[i].Owner);
      NewMenuItem.Action := Action;
      NewMenuItem.FileName := FMRUFileNames[j];
      FileName := ReplaceString(Intf.AbbreviatedFileName(NewMenuItem.FileName, False), '&', '&&');
      NewMenuItem.Caption := Format('&%d. %s', [j+1, FileName]);
      FParent[i].Insert(FMenuItemFinish[i].MenuIndex, NewMenuItem);
    end;
    FMenuItemStart[i].Visible := (Count>0) and (FMenuItemStart[i].MenuIndex>0);
    FMenuItemFinish[i].Visible := (FMenuItemFinish[i].MenuIndex<FParent[i].Count-1);
  end;
end;

procedure TMRU.Read;
var
  i: Integer;
  s: string;
begin
  if Registry.OpenKey(HKEY_CURRENT_USER, FRegistryKey) then begin
    FMRUFileNames.Clear;
    for i := 0 to MRUSize-1 do begin
      s := Registry.ReadString(IntToStr(i+1), '');
      if s<>'' then begin
        FMRUFileNames.Add(s);
      end;
    end;
    UpdateMenu;
    Registry.CloseKey;
  end;
end;

procedure TMRU.Write;
var
  i: Integer;
  ValueName: string;
begin
  if Registry.OpenKey(HKEY_CURRENT_USER, FRegistryKey, KEY_ALL_ACCESS, True) then begin
    Registry.WriteInteger('Size', MRUSize);
    for i := 0 to MRUSize-1 do begin
      ValueName := IntToStr(i+1);
      if i<Count then begin
        Registry.WriteString(ValueName, FMRUFileNames.Strings[i]);
      end else begin
        if Registry.ValueExists(ValueName) then begin
          Registry.DeleteValue(ValueName);
        end;
      end;
    end;
    Registry.CloseKey;
  end;
end;

function TMRU.GetCount: Integer;
begin
  Result := Min(FMRUFileNames.Count, MRUSize);
end;

function TMRU.GetItem(Index: Integer): string;
begin
  Result := FMRUFileNames[Index];
end;

procedure TMRU.SetAction(Value: TAction);
begin
  if Value<>FAction then begin
    FAction := Value;
    UpdateMenu;
  end;
end;

procedure TMRU.Add(const FileName: string);
var
  i, Index: Integer;
begin
  Index := -1;
  for i := 0 to FMRUFileNames.Count-1 do begin
    if FileNamesEqual(FileName, FMRUFileNames[i]) then begin
      Index := i;
      break;
    end;
  end;

  if Index<>-1 then begin
    FMRUFileNames.Move(Index, 0);
  end else begin
    FMRUFileNames.Insert(0, FileName);
    if FMRUFileNames.Count>MRUSize then begin
      FMRUFileNames.Delete(FMRUFileNames.Count-1);
    end;
  end;

  UpdateMenu;
  Write;
end;

procedure TMRU.Delete(ItemNum: Integer);
begin
  FMRUFileNames.Delete(ItemNum);
  UpdateMenu;
end;

initialization
  Registry := TRegistry.Create;
  if not Registry.KeyExists(AppRegistryKey) then begin
    Registry.CreateKey(AppRegistryKey);
  end;

finalization
  FreeAndNil(Registry);
3 голосов
/ 05 января 2011

В любом случае у вас будет отдельный TAction для каждого пункта меню, чтобы они могли иметь различные значения Caption.Но вам не нужно иметь отдельные обработчики событий OnExecute.Обработчик события получит ссылку на действие в своем параметре Sender.Используйте свойство Tag отправителя, чтобы обратиться к списку, в котором хранятся ваши имена файлов.(Не используйте свойство Caption для определения того, какой файл открывать; это ограничивает вас от таких полезных вещей, как добавление ускорителей или сокращение громоздких путей.)

Это то, что документация предполагает, что вы тоже будете делать,FindItemByAction возвращает первый элемент, к которому прикреплено данное действие.Если вы прикрепите одно действие ко всем пунктам меню MRU, вы не сможете использовать эту функцию, чтобы сообщить вам, какое меню было выбрано.С другой стороны, пункт меню не будет содержать больше информации, чем соответствующее действие, поэтому я не вижу смысла искать этот пункт в любом случае.Просто используйте информацию из акции напрямую.

...