初めまして。
ここ2週間ほど仕事で悩んでおり、ふと自宅で思いついた事をやってみたびですが、ちょっと解決しなさそうなのでお力をお貸し下さい。
OS:Windows7ProSP1(32ビット版)
言語:Delphi7Pro
フォームの HorzScrollBar プロパティの Visible を True に設定し、フォーム中にフォームより横幅が大きいパネルコンポーネントを貼ってます。
パネルコンポーネントには同じ横幅を持つ StringGrid を貼り、垂直方向のスクロールバーを表示しています。
そこで、次のように、フォームの再描画イベントでグリッドの垂直方向のスクロールバーを表示しています。
procedure TForm1.FormPaint(Sender: TObject);
begin
StringGrid1.Width := Form1.HorzScrollBar.Position + Form1.Width -18;
end;
ところが、フォームをリサイズしてパネルごと Height プロパティに値を代入すると、フォームの再描画イベントが発生しなくなります。具体的には、
procedure TForm1.FormResize(Sender: TObject);
begin
Panel1.Height := Form1.Height;
StringGrid1.Height := Panel1.Height;
end;
上記のコードを追加すると、再描画イベントが発生しなくなり、意図した動きが出来ません。
スクロールバーコンポーネントを使え、と言われそうですが、プログラムを複雑にしたくない(保守を容易にしたい)ので、なるべく簡潔で簡単な方法をとろうとしています。
OS と Delphi のバージョン(ちなみに Delphi7 はアップデータをかけていません。かけようと思うのですが、ボーランド社がなくなったので、出来ません)の問題なんでしょうか。
で、ちょっと考え方を変えて、フォームの水平スクロールバーが動いたタイミングで再描画をかければ問題ないと考え、色々調べましたが、自宅では限界です。
かと言って、職場でこの問題に割ける時間もないので、何かご存じの方、ヒントでもいいので教えていただけないでしょうか。
ちょっと強引ですが・・・
type
TForm1 = class(TForm)
・・・略・・・
private
{ Private 宣言 }
OriginalProc:TWndMethod;
procedure SubclassProc(var Msg:TMessage);
procedure WMHScroll(var Msg:TMessage);
public
{ Public 宣言 }
end;
・・・略・・・
procedure TForm1.FormCreate(Sender: TObject);
begin
OriginalProc := Form1.WindowProc;
Form1.WindowProc := SubclassProc;
end;
procedure TForm1.FormResize(Sender: TObject);
begin
Panel1.Height := Form1.Height;
StringGrid1.Height := Panel1.Height;
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
StringGrid1.Width := Form1.HorzScrollBar.Position + Form1.Width -18;
end;
procedure TForm1.WMHScroll(var Msg: TMessage);
begin
FormPaint(nil);
end;
procedure TForm1.SubclassProc(var Msg: TMessage);
begin
OriginalProc(Msg);
case Msg.Msg of
WM_HSCROLL: WMHScroll(TMessage(Msg));
end;
end;
とコーディングしたところ、解決しました。
フォームの水平スクロールバーが動くと WM_HSCROLL メッセージが発生するので、そのタイミングでフォームの再描画イベントを強制的にコールしてます。
ハッキリ言って余り良い解決法ではありません。これよりもより良い解決法があれば、ご教授下さい。お願いします。
とりあえず。
>ちなみに Delphi7 はアップデータをかけていません。かけようと思うのですが、ボーランド社がなくなったので、出来ません
正規ユーザーならEmbarcadero社が面倒見てくれてるので、問題なく出来ると思いますが…。
「Delphi7 アップデータ」で検索しましたか?
>上記のコードを追加すると、再描画イベントが発生しなくなり、意図した動きが出来ません。
FormResizeに Self.Invalidate; とか Self.Update; とか Self.Repaint; を追加じゃダメですか。。。。
私にはnabeさんの「意図した動き」がよく分からないんですが。
本質的にForm1.HorzScrollBarはユーザーに見せたいものなのか、それとも計算のためだけなのか??
Harryさん、レスありがとうございます。
水平方向のスクロールバー(Form1.HorzScrollBar)は、ユーザに見せますし、操作もさせます。
垂直方向のスクロールバーは、グリッドの垂直スクロールバーで済ませようと思っています。
なるほど、フォームのリサイズイベントハンドラで Self.Repaint すると、ちゃんと再描画してくれますね。ありがとうございます。
もっと踏み込むと、フォームのスクロールバーを操作すると、何らかのイベントやメッセージが取れるのではないか?と考えます。
そこで昨日自宅で、
>フォームの水平スクロールバーが動くと WM_HSCROLL メッセージが発生するので、そのタイミングでフォームの再描画イベントを強制的にコールしてます。
というコーディングをしましたが、パネルにエディットコンポーネントが多数載っていて、TABキーによってフォームがスクロールしてしまう分には WM_HSCROLL メッセージが発生しないようです。
この場合、どのように対処したら良いのでしょうか。
Mr.XRay さんのサイトも参考にはしたいのですが・・・あくまでもGUIベースで簡単に、を目指しているので、引き続きお知恵をお貸し下さい。
あの、、、アップデータはどうなりましたでしょうか…。
私はnabeさんの目指してるUIの形をおそらく10%程度しか想像できていないのですが、、、
フォームの標準スクロールバー位置の変更は↓これで検知して利用できませんか?
private
procedure WMWindowPosChanging(var Message: TWMWindowPosMsg); message WM_WINDOWPOSCHANGING;
をしておいて、メッセージメソッド
procedure TForm1.WMWindowPosChanging(var Message: TWMWindowPosMsg);
begin
if (Message.WindowPos.flags and SWP_NOSIZE)=0 then
Self.Caption:=Format('Horz=%d, Vert=%d',
[Self.HorzScrollBar.Position, Self.VertScrollBar.Position]);
inherited;
end;
しかし、とりあえずこれでうまくいったとしても、また何か次の問題が発生するんじゃあ…泥沼化の懸念。
>あくまでもGUIベースで簡単に、を目指しているので
簡単に、と言うなら何か根本がおかしい気がします。私はUIの経験が乏しいのでハッキリとは指摘できませんが。
普通は適材適所のコンポを適切に配置したり、位置・サイズ関係のプロパティやイベントで対処するのではないかと。
(フォームのスクロールバーは必須ですか? StringGridのスクロールではダメ? EditはScrollBoxに納めるとか。)
Harryさん、レスありがとうございます。
返信が遅くなりましたが、意図するような動作になりました。
元々のプログラムがフォームより大きいStringGridを貼っていて、そのグリッドの固定列のすぐ上にEditコンポーネントを列数分貼っているという、とんでもないモノだったため、プログラムはグジャグジャだし、メンテナンスに耐えるものではありませんでした(その上、固定列をマウスで可変させると、その上のEditコンポーネントの幅が連動して変わる、という仕様です)。
水平・垂直方向にスクロールバーコンポーネントを貼り、コードで制御しているので、他人が手をつけられたモノではありません。
そこで、垂直方向のスクロールバーをStringGridのスクロールバーにし、水平方向のスクロールバーをフォームに変更して、メンテナンスしやすいプログラムに作り替える必要がありました。
(グリッドが横スクロールすると、上にエディットも同様にスクロールしなければならないため。)
流石にこのUIは如何なものか?と言いたい所ですが、このプログラムには熱狂的ユーザがいるそうで、UIは変更出来なかったんです。
最後にアップデータの件ですが、間もなくXE2に移行する予定なので、放置状態です。
>このプログラムには熱狂的ユーザがいるそうで、UIは変更出来なかったんです。
(^_^;;;;;;;;;;;;;;;;
WM_WINDOWPOSCHANGINGについて。
これは確かにフォームの標準スクロールバーの位置変更を捕捉できますが、同じ内容が複数回送られて来たり、
フォーム上のコントロール(ScrollBox等)の上にある標準スクロールバーの分も来たりします。
最もイマイチなのが、水平と垂直が同時に変化したとき、その変化途中の分まで送ってくる点です。
水平70・垂直0 → 水平0・垂直0 → 水平0・垂直200 みたいな。(水平0・垂直0 は必要ないのに〜)
まあ、再描画のタイミング取りの用途なら問題にならないようですが。
手堅くするなら、単純に Application.OnIdle(または ApplicationEvents.OnIdle)の方が良いかも知れません。
ツイート | ![]() |