[Delphi] Windows 'open file' dialoogvenster aanpassen

Pagina: 1
Acties:

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 20:47

Tomatoman

Fulltime prutser

Topicstarter
In Delphi (Win32) is het standaard Windows 'open file' dialoogvenster beschikbaar via TOpenDialog. Nu wil ik onder de read-only checkbox een eigen checkbox plaatsen waarmee je kunt aangeven dat je het bestand met exclusieve toegang wilt openen. Op dit moment heb ik de volgende aanpak:
  • Een eigen descendant van TOpenDialog maken: TSpecialOpenDialog
  • Een TPanel creëren zonder parent.
  • Een TCheckBox creëren met het panel als parent.
  • Als er een window handle voor het dialoogvenster is het panel op het venster plaatsen door aan de ParentWindow property van het panel de handle toe te wijzen.
  • Het panel op zijn plaats zetten.
Nu krijg ik wel een zichtbaar panel met daarbovenop de checkbox, alleen zijn er weergaveproblemen. Ten eerste worden panel en checkbox pas zichtbaar als het dialoogvenster geresized wordt. Zelfs InvalidateRect voor het venster of een WM_PAINT message naar het venster sturen maakt geen verschil. Ten tweede zie je soms na het resizen de restanten van de vorige weergave er nog doorheen. Ten derde zit er om de checkbox geen focus rectangle, zelfs als hij de focus heeft.

Afbeeldingslocatie: http://img508.imageshack.us/img508/2965/crap7st.jpg

Het panel vervangen door een TForm met BorderStyle = bsNone maakt niets uit, de tekenproblemen blijven bestaan.

Ziet iemand hoe dit probleem valt op te lossen of te omzeilen?

Een goede grap mag vrienden kosten.


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Geen idee of je er iets aan hebt, maar hier al eens gekeken? En, for once probeer ik nu eens een keer niet de smart-ass uit te hangen, want ik heb geen idee of je hier in Delphi iets aan hebt.

En anders hier misschien...

Waarom ik er mee kom? Ik zou zweren dat er een flag was voor die "exclusive" checkbox (als in: zit er native al in), maar zou bij God niet meer weten hoe/wat/waar :P

...Of toch niet :? Weet het ook niet meer...

Ik dacht eigenlijk dat Access dat (vroeger?) gebruikt(e), maar Access 2003 lost het nu zo op:
Afbeeldingslocatie: http://tweakers.net/ext/f/cf2f4b425c11225b0424fd7eb815de04/full.gif

[ Voor 58% gewijzigd door RobIII op 18-11-2005 23:53 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 20:47

Tomatoman

Fulltime prutser

Topicstarter
De drie pagina's die jij noemt had ik al uitgespit. Ik gebruik geen template om het dialoogvenster te customizen en wel om twee redenen. Ten eerste zet ik daarmee de VCL overboord, waardoor ik zogezegd in het stenen tijdperk terugkeer. Aangezien in Delphi nooit templates worden gebruikt is de kans dat ik iets fout doe wel erg groot (ik ken mijn template skills :X).

Ten tweede maak je met een template het dialoogvenster groter, terwijl ik daar helemaal niet op zit te wachten. Ik wil er alleen een checkbox bijzetten en wel binnen de oude vensterdimensies, niet daarbuiten.

Dat ding van Access had ik ook nog over zitten denken, maar dat verplaatst alleen het probleem. Ik moet dan de standaard 'open' button vervangen door mijn eigen button.

Een goede grap mag vrienden kosten.


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Ik begrijp het. En toch blijf ik maar twijfelen. Ik vreet een beer als oudere Access versies niet ook gewoon een checkbox hadden zoals jij dat graag wil. (Dat was dus voor de derde keer in 10 minuten dat ik shackbox typte :X Waar het hart van vol is... :P ). Ik zoek nog effe verder, want volgens mij zit 't dus standaard in Windows (en dan neem ik dus even aan dat je met een flag ofzo die optie dus ook in Delphi kunt inschakelen?)

Ander idee: Heb je dat read-only vinkje nodig? Anders zou je die gewoon kunnen "misbruiken" en de tekst er van wijzigen?

[ Voor 17% gewijzigd door RobIII op 19-11-2005 00:07 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 20:47

Tomatoman

Fulltime prutser

Topicstarter
Ja, helaas heb ik de read-only checkbox ook nodig. Anders zou het wel erg simpel zijn geweest :P. En volgens de Platform SDK zit er geen tweede checkbox in het standaard dialoogvenster.

Een goede grap mag vrienden kosten.


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
tomatoman schreef op zaterdag 19 november 2005 @ 00:22:
Ja, helaas heb ik de read-only checkbox ook nodig. Anders zou het wel erg simpel zijn geweest :P. En volgens de Platform SDK zit er geen tweede checkbox in het standaard dialoogvenster.
* RobIII moet toch eens iets aan z'n slechter wordend geheugen gaan doen ;)

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 20:47

Tomatoman

Fulltime prutser

Topicstarter
RobIII schreef op zaterdag 19 november 2005 @ 00:25:
[...]

* RobIII moet toch eens iets aan z'n slechter wordend geheugen gaan doen ;)
Ja RobIII, dit kan echt niet! :P

Een goede grap mag vrienden kosten.


  • martijn_brinkers
  • Registratie: November 2001
  • Laatst online: 31-10-2025
Heb je al gekeken naar TOpenPictureDialog? Die subclassed nl TOpenDialog om er een image view bij te plaatsen

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 20:47

Tomatoman

Fulltime prutser

Topicstarter
TijnFLiP schreef op zaterdag 19 november 2005 @ 00:45:
Heb je al gekeken naar TOpenPictureDialog? Die subclassed nl TOpenDialog om er een image view bij te plaatsen
Die heb ik inderdaad bestudeerd. De truc die daar wordt uitgehaald is dat de venstergrootte vast is, zodat ze geen problemen met de weergave hebben. Bovendien kan geen van de bijgeplaatste elementen de focus krijgen. Ik weet niet of dit toeval is of dat hier expres voor is gekozen als workaround. Bovendien wordt daar een template gebruikt, zodat de controls buiten de standaard vensterdimensies geplaatst worden. Verder doe ik voor zover ik kan nagaan alles hetzelfde, ik zie niet waarom het foutgaat.

Een goede grap mag vrienden kosten.


  • martijn_brinkers
  • Registratie: November 2001
  • Laatst online: 31-10-2025
Ik heb wat lopen knippen en plakken in een kopie van OpenPictureDialog voor een poc. Weet niet of dat is wat je bedoelde (let op! code is snel in elkaar ;-)

De volgende functies heb ik wat aangepast van een kopie van OpenPictureDialog.

Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
constructor TMyOpenPictureDialog.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Filter := GraphicFilter(TGraphic);
  FPicturePanel := TPanel.Create(Self);
  with FPicturePanel do
  begin
    Name := 'PicturePanel';
    Caption := '';
    SetBounds(400, 10, 100, 110);
    BevelOuter := bvNone;
    BorderWidth := 1;
    TabOrder := 1;
    Left := 0;
    Top := 0;
  FCB := TCheckBox.Create( Self );
  FCB.Left := 585;
  FCB.Top  := 0;
  FCB.Parent := FPicturePanel;
  FCB.Caption := 'Test 123';

  end;
end;

procedure TMyOpenPictureDialog.DoShow;
var
  PreviewRect, StaticRect: TRect;
begin
  inherited DoShow;

//  { Set preview area to entire dialog }
  GetClientRect(Handle, PreviewRect);
  StaticRect := GetStaticRect;
//  { Move preview area to right of static area }
    PreviewRect.Left := StaticRect.Left + (StaticRect.Right - StaticRect.Left);
  PreviewRect.Left := -400;
  PreviewRect.Top := 380;
  Inc(PreviewRect.Top, 4);
  FPicturePanel.BoundsRect := PreviewRect;
  FPicturePanel.ParentWindow := Handle;
end;


Resultaat:
Afbeeldingslocatie: http://www.mediaupload.net/images/2005/Nov/19/EFcFVf8ISgV3thb.jpg

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 20:47

Tomatoman

Fulltime prutser

Topicstarter
TijnFLiP schreef op zaterdag 19 november 2005 @ 01:57:
Ik heb wat lopen knippen en plakken in een kopie van OpenPictureDialog voor een poc. Weet niet of dat is wat je bedoelde (let op! code is snel in elkaar ;-)
Jazeker, dat is wat ik bedoelde. Mijn code ziet vergelijkbaar uit. Het resultaat is zoals ik beschreven had - met weergaveproblemen dus :/. Ik heb sterk de indruk dat er een zootje WM_PAINT of WM_SIZEWINDOW messages verdwijnt.

Een goede grap mag vrienden kosten.


  • martijn_brinkers
  • Registratie: November 2001
  • Laatst online: 31-10-2025
Het lijkt bij mij goed te gaan. Ik hoef niet eerst te resizen om de checkbox te zien en bij resizen blijft het er netjes uit zien (dus geen 'leftovers' zoals jij vermeld). De vraag is nu, wat is het verschil?

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 20:47

Tomatoman

Fulltime prutser

Topicstarter
Met veel klooien heb ik het volgende uitgevonden.
tomatoman schreef op vrijdag 18 november 2005 @ 23:38:
Ten eerste worden panel en checkbox pas zichtbaar als het dialoogvenster geresized wordt. Zelfs InvalidateRect voor het venster of een WM_PAINT message naar het venster sturen maakt geen verschil. Ten tweede zie je soms na het resizen de restanten van de vorige weergave er nog doorheen.
Dit probleem verdwijnt als sneeuw voor de zon als je een template gebruikt, ongeacht of de controls in de template zijn gedefinieerd. Het lijkt erop dat Windows de ontbrekende WM_PAINT messages alleen verstuurt als er een template wordt gebruikt.
Ten derde zit er om de checkbox geen focus rectangle, zelfs als hij de focus heeft.
Dit blijkt een rare eigenschap van een TPanel. Als de checkbox in een TGroupBox staat is de focus rectangle er weer.

Uiteindelijk heb ik het probleem opgelost door een lege template te gebruiken. De standaard 'open as read-only' check box van het dialoogvenster verberg ik en in plaats daarvan zet ik een eigen 'open as read-only' checkbox neer met daaronder een 'open exclusively' check box. De reden om een eigen check box te gebruiken is dat het anders niet lukt om ze in de tab order direct achter elkaar te krijgen. Het resultaat:

Afbeeldingslocatie: http://img389.imageshack.us/img389/1561/dlg7mf.jpg

Er bleken nog heel wat andere addertjes onder het gras te zittten, vandaar dat ik hier de complete code post.

De .RC file:
code:
1
2
3
4
5
SPECIALOPENDIALOGTEMPLATE DIALOG 0, 0, 320, 34
STYLE WS_CHILD | WS_CLIPSIBLINGS | DS_3DLOOK | DS_CONTROL  
FONT 8, "MS Sans Serif"
BEGIN
END


De Delphi unit:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
{-------------------------------------------------------------------------------

        Specialized dialogs

        Copyright (c) 2005 Robin Gerrets. All rights reserved.
________________________________________________________________________________

  TSpecialOpenDialog is a dialog with two extra check boxes:
    - 'Open as read-only' (replaces the default read-only check box)
    - 'Open exclusively'
  These check boxes can be accessed via the ReadOnly and Exclusive properties.
  The ReadOnly property maps to Options.ofReadOnly.

-------------------------------------------------------------------------------}

unit SpecialDlg;

interface

uses Messages, Windows, SysUtils, Classes, Controls, StdCtrls, ExtCtrls,
  Forms, Dialogs, Dlgs;

type
  TSpecialOpenDialog = class(TOpenDialog)
  private
    FBasePanel: TPanel;
    FExclusive: Boolean;
    FExclusiveCheck: TCheckBox;
    FOpenModeBox: TGroupBox;
    FReadOnly: Boolean;
    FReadOnlyCheck: TCheckBox;
    function GetOptions: TOpenOptions;
    procedure SetExclusive(Value: Boolean);
    procedure SetOptions(Value: TOpenOptions);
    procedure SetReadOnly(Value: Boolean);
    procedure ExclusiveClick(Sender: TObject);
    procedure ReadOnlyClick(Sender: TObject);
  protected
    procedure DoClose; override;
    procedure DoResizeBasePanel;
    procedure DoShow; override;
    procedure FixupZOrder(WinControl: TWinControl);
    procedure WndProc(var Message: TMessage); override;
    property BasePanel: TPanel read FBasePanel;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function Execute: Boolean; override;
  published
    property Exclusive: Boolean read FExclusive write SetExclusive;
    property Options read GetOptions write SetOptions default [ofEnableSizing];
    property ReadOnly: Boolean read FReadOnly write SetReadOnly stored False;
  end;

resourcestring
  SSpecialOpenMode = 'Open mode';
  SSpecialOpenReadOnly = 'Open as &read-only';
  SSpecialOpenExclusive = 'Open e&xclusively';

implementation

{$R *.RES}

function TSpecialOpenDialog.GetOptions: TOpenOptions;
begin
  Result := inherited Options - [ofHideReadOnly];
  if FReadOnly then
    Include(Result, ofReadOnly)
  else
    Exclude(Result, ofReadOnly);
end;

procedure TSpecialOpenDialog.SetExclusive(Value: Boolean);
begin
  if FExclusive <> Value then
  begin
    FExclusive := Value;
    FExclusiveCheck.Checked := Value;
  end;
end;

procedure TSpecialOpenDialog.SetOptions(Value: TOpenOptions);
begin
  inherited Options := Value + [ofHideReadOnly];
  ReadOnly := ofReadOnly in Value;
end;

procedure TSpecialOpenDialog.SetReadOnly(Value: Boolean);
begin
  if FReadOnly <> Value then
  begin
    FReadOnly := Value;
    FReadOnlyCheck.Checked := Value;
  end;
end;

procedure TSpecialOpenDialog.ExclusiveClick(Sender: TObject);
begin
  FExclusive := FExclusiveCheck.Checked;
end;

procedure TSpecialOpenDialog.ReadOnlyClick(Sender: TObject);
begin
  FReadOnly := FReadOnlyCheck.Checked;
end;

procedure TSpecialOpenDialog.DoClose;
begin
  inherited DoClose;
  { Hide any hint windows left behind }
  Application.HideHint;
end;

procedure TSpecialOpenDialog.DoResizeBasePanel;
var
  FileTypeCombo: THandle;
  FileTypeComboRect: TRect;
begin
  { Retrieve the window rectangle for the file type combo box, which has ID
    cmb1 }
  FileTypeCombo := GetDlgItem(GetParent(Handle), cmb1);
  GetWindowRect(FileTypeCombo, FileTypeComboRect);
  MapWindowPoints(0, Handle, FileTypeComboRect, 2);

  { Resize the base panel width to the width of the combo box + label }
  FBasePanel.Width := FileTypeComboRect.Right - FBasePanel.Left;
end;

procedure TSpecialOpenDialog.DoShow;
var
  FileTypeLbl: THandle;
  FileTypeLblRect: TRect;
begin
  { Retrieve the window rectangle for the label of the file type combo box,
    which has ID stc2 }
  FileTypeLbl := GetDlgItem(GetParent(Handle), stc2);
  GetWindowRect(FileTypeLbl, FileTypeLblRect);
  MapWindowPoints(0, Handle, FileTypeLblRect, 2);

  FBasePanel.Left := FileTypeLblRect.Left;
  FBasePanel.Top := FileTypeLblRect.Top + 30;
  FBasePanel.Height := 64;
  DoResizeBasePanel; // Initialize the base panel's width
  FBasePanel.ParentWindow := Handle;

  { The captions are reset to empty strings every time the dialog box is closed.
    Set the captions whenever the dialog box is shown. }
  FOpenModeBox.Caption := ' ' + SSpecialOpenMode + ' ';
  FReadOnlyCheck.Caption := SSpecialOpenReadOnly;
  FExclusiveCheck.Caption := SSpecialOpenExclusive;

  inherited DoShow;
end;

procedure TSpecialOpenDialog.FixupZOrder(WinControl: TWinControl);
var
  TabList: TList;
  i: Integer;
begin
  { By default the dialog uses the order in which the windowed controls were
    added as the tab order, where the last added control is placed first in the
    tab order. Rearrange the Z-order of the windowed controls to make the
    controls reflect the tab order as set by their TabOrder property. }
  try
    TabList := TList.Create;
    try
      WinControl.GetTabOrderList(TabList);
      for i := 0 to TabList.Count -1 do
      try
        TWinControl(TabList.Items[i]).SendToBack;
      except
      end;
    finally
      TabList.Free;
    end;
  except
  end;
end;

procedure TSpecialOpenDialog.WndProc(var Message: TMessage);
begin
  inherited;
  if Message.Msg = WM_SIZE then
    DoResizeBasePanel;
end;

constructor TSpecialOpenDialog.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FBasePanel := TPanel.Create(nil);
  with FBasePanel do
  begin
    Caption := '';
    BevelOuter := bvNone;
    BorderWidth := 0;
    TabStop := True;
    Font.Name := 'MS Shell Dlg';
    FullRepaint := False;
  end;

  FOpenModeBox := TGroupBox.Create(FBasePanel);
  with FOpenModeBox do
  begin
    Align := alClient;
    Parent := FBasePanel;
  end;

  FReadOnlyCheck := TCheckBox.Create(FBasePanel);
  with FReadOnlyCheck do
  begin
    Checked := ofReadOnly in Options;
    Left := 8;
    Top := 20;
    Width := FOpenModeBox.Width - 40;
    Parent := FOpenModeBox;
    OnClick := ReadOnlyClick;
  end;

  FExclusiveCheck := TCheckBox.Create(FBasePanel);
  with FExclusiveCheck do
  begin
    Checked := FExclusive;
    Left := 8;
    Top := FReadOnlyCheck.Top + FReadOnlyCheck.Height + 4;
    Width := FOpenModeBox.Width - 40;
    Parent := FOpenModeBox;
    OnClick := ExclusiveClick;
  end;

  FixupZOrder(FBasePanel);
end;

destructor TSpecialOpenDialog.Destroy;
begin
  FreeAndNil(FReadOnlyCheck);
  FreeAndNil(FExclusiveCheck);
  FreeAndNil(FOpenModeBox);
  FreeAndNil(FBasePanel);
  inherited Destroy;
end;

function TSpecialOpenDialog.Execute: Boolean;
begin
  if not NewStyleControls or (ofOldStyleDialog in Options) then
    Template := nil
  else
    Template := 'SPECIALOPENDIALOGTEMPLATE';
  Result := inherited Execute;
end;

end.

[ Voor 16% gewijzigd door Tomatoman op 26-11-2005 19:52 ]

Een goede grap mag vrienden kosten.

Pagina: 1