2006. június 15., csütörtök

How to prevent the use of the listview's popup menu in a TOpenDialog


Problem/Question/Abstract:

Using the TOpenDialog and TSaveDialog components you get the standard Windows dialog boxes including the ability to create/ delete files or folders. Is it possible to block the create/ delete options?

Answer:

Well, few things are really impossible if you try hard enough, but this is at least somewhat difficult. The common dialog API has no facility to do this, the dialogs simply use the same listview class Explorer also uses, so it has all the same functionality. So to block these functions one would have to subclass (the API way) the listview control (to block right mouse clicks and the offending keyboard messages as well as WM_CONTEXTMENU). One could do that in the dialogs OnShow event. The problem is finding the handle of the listview, these Explorer-style dialogs have an utterly weird internal window hierarchy. And the listview in question has not been created yet when the OnShow event fires (go figure). So you have to post a user message to the form from the OnShow event and do the subclassing in that messages handler.

Here is a quick sketch of a modified opendialog class that prevents the use of the listviews popup menu. For some reason it is not possible to trap the DEL key press on the listview level, so if you want to also trap that, and perhaps even the editing of filenames, you will also need to subclass the shellview, which is the parent of the listview, and look for WM_NOTIFY messages from the listview there.

{ ...}
type
  TSafeOpenDialog = class(Dialogs.TOpenDialog)
  private
    FOldListviewProc: Pointer;
    FListviewMethodInstance: Pointer;
    FLIstview: HWND;
    procedure WMApp(var msg: TMessage); message WM_APP;
  protected
    procedure DoShow; override;
    procedure ListviewWndProc(var msg: TMessage);
  public
    destructor Destroy; override;
  end;

destructor TSafeOpenDialog.Destroy;
begin
  inherited;
  if Assigned(FListviewMethodInstance) then
    FreeObjectInstance(FListviewMethodInstance);
end;

procedure TSafeOpenDialog.DoShow;
begin
  inherited;
  PostMessage(handle, WM_APP, 0, 0);
end;

procedure TSafeOpenDialog.ListviewWndProc(var msg: TMessage);
begin
  msg.result := 0;
  case msg.Msg of
    WM_RBUTTONDOWN, WM_RBUTTONUP, WM_CONTEXTMENU:
      Exit;
  end;
  msg.result := CallWindowProc(FOldListviewProc, FLIstview, msg.Msg,
    msg.WParam, msg.LParam);
end;

procedure TSafeOpenDialog.WMApp(var msg: TMEssage);
begin
  FListviewMethodInstance := MakeObjectInstance(ListviewWndProc);
  FListview := FindWindowEx(Windows.GetParent(handle), 0, 'SHELLDLL_DefView', nil);
  if FListview <> 0 then
  begin
    FListview := GetWindow(FListview, GW_CHILD);
    if FListview <> 0 then
      FOldListviewProc := Pointer(SetWindowLong(FListview, GWL_WNDPROC,
        Integer(FListviewMethodInstance)))
    else
      OutputDebugString('Listview not found');
  end
  else
    OutputDebugString('Shell view not found');
end;

Nincsenek megjegyzések:

Megjegyzés küldése