2012-02-25 24 views
9

バグかどうかわかりませんが、 "Windows"以外のVCLスタイルを設定すると、ウィンドウの幅が狭くなります。VCLスタイル - クライアントのサイズを小さくしました

Windows style Any other style looks like this... -

このため任意の解決策はありますか?

UPDATE は私がQCにこれを提出した:彼らはそれを修正しますhttp://qc.embarcadero.com/wc/qcmain.aspx?d=103697 ホープ...

+0

どのバージョンのDelphiですか? –

+0

これはプレーンなアプリでは再現できません。動作がなくなるまで、フォーム/アプリケーションから削除するようにしてください。次に、あなたは犯人にポインタを持っているかもしれません。 –

+1

@CosminスタイルはXE2で導入されました。 –

答えて

4

確かにVCLのバグのように見えるん。スタイルがプロジェクトオプションでシステムスタイル以外に設定されている場合、ClientWidthプロパティが.dfmファイルから正しくストリーミングされません。

QualityCentralに報告書を提出することをお勧めします。その間に、フォームが作成された後に.dprファイルのスタイルを設定することで、この問題を回避することができます。

Application.Initialize; 
Application.MainFormOnTaskbar := True; 
Application.CreateForm(TMainForm, MainForm); 
TStyleManager.SetStyle('Amakrits');//after CreateForm, rather than before 
Application.Run; 

しかし、私はそれが非常に遠く、おそらくその場でフォームを作成し、すべての起動時に作成する必要はありませんできるようにしたいので、あなたを得るでしょう想像していません。

+0

それは私にとってはうまくいかなかった。スタイルを設定すると、フォームが伸びます。私はそれをQCに報告します... – djsoft

+0

プレーンなバニラアプリでうまくいくのですか? –

+0

@DavidHefferman、これはバグではありません、私の答えを確認してください。 – RRUZ

0

ここにXE2はありませんが、これは非常によく知られています。 AutoScrollを真の(奇妙なことに、this answerの逆)に設定して、境界線サイズではなくクライアントフォームサイズを保存してみてください。

+0

'AutoScroll'はデフォルトで' False'になり、その状態でバグが発生します。実際に 'AutoScroll'を' True'に設定すると、プログラムが期待どおりに動作するようになります! –

+1

@David Hmm、strange。これはXE2(またはそれ以前)で変更されたようです。 D7では、 'AutoScroll'のデフォルトは' True'です。 – NGLN

+0

2010年にFalseにデフォルト設定されていますが、D7とD2010の間にバージョンがありません –

4

これは、VCLスタイルのバグ、 これは、VCLスタイルがどのように機能するかを、各スタイル(スキン)は時々ネイティブウィンドウの境界線のサイズと一致しない独自の境界線の幅と高さを、持っているではありません。

チェックを次の画像

enter image description here

炭素スタイルは5つの画素

enter image description here

Amakritsスタイルは6つのピクセルの境界線の幅と高さを有するの境界線の幅と高さを有します

enter image description here

あなたはVCL Styles Designer

  • オブジェクトを使用して、各スタイルの境界線スタイルのサイズを確認することができます - >フォーム - >画像 - > LeftBorder - >幅
  • オブジェクト - >フォーム - >画像 - > RigthBorder - >幅
  • オブジェクト - >フォーム - >画像 - > BottomBorder - だから、上記の特性に依存し、フォームのスタイルフックがクライアント領域の境界を再計算

>高さ。

+1

本当にバグです。値の境界はOSのバージョンによって異なりますが、ClientWidthは常にシステムスタイルで正しくストリーミングされます –

+0

スタイルのバグではないかもしれませんが、間違いなくどこかのバグであるクライアントの幅が正しく計算されない – djsoft

+1

@DavidHeffernan、私の答えは、なぜクライアントの高さとクライアントwidhtがvclスタイルが適用されたときに変更されるのかを説明しています。 – RRUZ

0

この問題はまだDelphi XE8にあります。簡単な回避策はいくつかの注意事項(最も重要なのは、AutoScrollFalseに設定する必要があります)で、ちょうどClientWidth/ClientHeight設計時に復元し、次のコードを使用することです:

次設計時のフォームを考えると
type 
    TFormHelper = class helper for Vcl.Forms.TCustomForm 
    private 
    procedure RestoreDesignClientSize; 
    end; 

procedure TfrmTestSize.FormCreate(Sender: TObject); 
begin 
    RestoreDesignClientSize; 
end; 

{ TFormHelper } 

procedure TFormHelper.RestoreDesignClientSize; 
begin 
    if BorderStyle in [bsSingle, bsDialog] then 
    begin 
    if Self.FClientWidth > 0 then ClientWidth := Self.FClientWidth; 
    if Self.FClientHeight > 0 then ClientHeight := Self.FClientHeight; 
    end; 
end; 

design time form

これはからランタイムを修正します。

enter image description here

へ:

enter image description here

私は私のブログに詳細と写真を持っている:http://marc.durdin.net/2015/07/fixing-the-incorrect-client-size-for-delphi-vcl-forms-that-use-styles/

+0

私たちにお知らせいただきありがとうございます。私はXE8のアップデート(XE7と同様)の代金を支払わなかったことをうれしく思っています。私はこの問題についてQCレポートをチェックしましたが、2012年に投稿されましたがまだ修正されていません... – djsoft

0

OKを - 私はいくつかのより多くの調査を行なったし、このバグの根本的な問題を発見した(最後までスキップ回避策を参照してください)。インターネット上に散在し、このメッセージの前に議論された他の回避策の大半またはすべては、根本的な原因を実際に発見することなく、バグの症状を隠しているように見えますが、これらの回避策には他の望ましくない副作用や制限彼らの著者のいくつかが指摘しているように)。

根本的な問題はwParamパラメータがFALSEときTFormStyleHook.WMNCCalcSizeメッセージがWM_NCCALCSIZEメッセージのいずれかの処理を提供しないということです。機能は基本的に不完全です。 デフォルトのウィンドウハンドラは - Windows提供のデフォルトハンドラ - もちろんは、ユーザが指定したVCLスタイルではなくWindows-defaultスタイルのクライアント矩形を返します。このバグを修正するにはwParamFALSEの場合、EmbarcaderoはWM_NCCALCSIZEの処理を追加して、VCLスタイル情報が返されるようにする必要があります。これは非常に簡単な修正であり、私が調査して問題を発見したので、修正がVCLの次のリリースに適用されることを願っています。

これが問題の原因だったことを証明するために、私は(WndProcをオーバーライドすることによって)フォームに送信されたすべてのメッセージをログに記録され、各メッセージのために、Win32のGetClientRectによって提供されるクライアントRECTは、VCLスタイルのために正しかったかどうかを指摘しました。私はまた、関数呼び出しのタイプ(wParamの値)に注意しました。最後に、WM_NCCALCSIZEハンドラによって返された新しいクライアント矩形に注目しました。

WM_NCCALCSIZEメッセージのほとんどすべてがTRUETRUE)に設定されているため、このバグは表示されないため、表示されません。そのため、Embarcaderoは今までこのバグを解決してきました。しかし、メッセージはwParamがに設定された状態で送信され、重要な瞬間にメッセージが送信されます./ClientHeightのプロパティの前には、ファイルの値がTCustomForm.ReadStateに設定されます。 TControl.SetClientSize関数は、現在のウィンドウ幅から現在のクライアント幅(Windows GetClientRectで測定)を減算して動作し、新しいクライアント幅を加算します。 つまり、TControl.SetClientSizeは、新しいクライアントrectを計算するために現在のウィンドウクライアントrectを正確にする必要があります。そして、そうではないので、フォームは間違った幅のセットを取得し、残りは履歴です。

なぜ、幅に影響があり、高さではないのだろうか?それは証明するのが容易でした。ClientWidthが設定された後、ClientHeightが設定される前に判明しました。別のWM_NCCALCSIZEが送信されました。今回はwParamTRUEで送信されます。 VCLスタイルで正しく処理され、クライアントのサイズが適切な値に戻されます。したがって、ClientHeightの計算が正しく行われます。フォームが表示されている間も、Microsoftは、より定期的にFALSEwParamセットでWM_NCCALCSIZEメッセージを送信することを決定した場合、物事はVCLのために非常にひどく破損します:Windowsの将来のバージョンは、よりひどく壊すかもしれない

注意。

手動でフォームにWM_NCCALCSIZEを送信することで、バグを簡単に突き止めることができます。再現する手順:

  1. C++ Builderの新VCLフォームアプリケーションを作成します。
  2. プロジェクトオプション外観セクションからカーボン VCLスタイルに現在/デフォルトVCLスタイルを設定します。
  3. 新しいTButtonコントロールをフォームに追加します。
  4. は、ボタンのOnClickイベントに次のコードを追加します

    void __fastcall TForm1::Button1Click(TObject *Sender) 
    { 
        // Compute the current cumulative width of the form borders: 
        int CurrentNonClientWidth = Width - ClientWidth; 
        // Get the current rectangle for the form: 
        TRect rect; 
        ::GetWindowRect(Handle, &rect); 
        // Ask the window to calculate client area from the window rect: 
        SendMessage(Handle, WM_NCCALCSIZE, FALSE, (LPARAM)&rect); 
        // Calculate the new non-client area given by WM_NCCALCSIZE. It *should* 
        // match the value of CurrentNonClientWidth. 
        int NewNonClientWidth = Width - rect.Width(); 
        if (CurrentNonClientWidth == NewNonClientWidth) { 
         ShowMessage("Test pass: WM_NCCALCSIZE with wParam FALSE gave " 
          "the right result."); 
        } else { 
         ShowMessage(UnicodeString::Format(L"Test fail: WM_NCCALCSIZE with " 
          "wParam FALSE gave a different result.\r\n\r\nCurrent NC width: %d" 
          "\r\n\r\nNew NC width: %d", ARRAYOFCONST((
          CurrentNonClientWidth, NewNonClientWidth)))); 
        } 
    } 
    
  5. は、プロジェクトを実行し、ボタンをクリックしてください。合格テストを受けると、VCLスタイルのNC幅がデフォルトのWindows NC幅と一致することを意味します。フォームの境界線スタイルを変更するか、VCLスタイルを別のものに変更して、もう一度やり直してください。

この問題を回避するには、当然のことながら、wParamFALSEあるWM_NCCALCSIZEメッセージを傍受し、その後wParamTRUEあるメッセージに変換する方法を見つけることです。これは実際にはグローバルベースで行うことができます:TFormStyleHookの派生クラスを作成して問題を修正し、フックをグローバルに使用することができます。これにより、VCLで作成したフォーム(Vcl.Dialogsなど)単位)。上に示したサンプルプロジェクトでは、次のようにメインProject1.cppを変更します。

//--------------------------------------------------------------------------- 

#include <vcl.h> 
#pragma hdrstop 
#include <tchar.h> 
#include <string.h> 
#include <Vcl.Forms.hpp> 
//--------------------------------------------------------------------------- 
#include <Vcl.Styles.hpp> 
#include <Vcl.Themes.hpp> 
USEFORM("Unit1.cpp", Form1); 
//--------------------------------------------------------------------------- 
class TFixedFormStyleHook : public TFormStyleHook 
{ 
public: 
    __fastcall virtual TFixedFormStyleHook(TWinControl* AControl) 
     : TFormStyleHook(AControl) {} 
protected: 
    virtual void __fastcall WndProc(TMessage &Message) 
    { 
     if (Message.Msg == WM_NCCALCSIZE && !Message.WParam) { 
      // Convert message to format with WPARAM == TRUE due to VCL styles 
      // failure to handle it when WPARAM == FALSE. Note that currently, 
      // TFormStyleHook only ever makes use of rgrc[0] and the rest of the 
      // structure is ignored. (Which is a good thing, because that's all 
      // the information we have...) 
      NCCALCSIZE_PARAMS ncParams; 
      memset(&ncParams, 0, sizeof(ncParams)); 
      ncParams.rgrc[0] = *reinterpret_cast<RECT*>(Message.LParam); 

      TMessage newMsg; 
      newMsg.Msg = WM_NCCALCSIZE; 
      newMsg.WParam = TRUE; 
      newMsg.LParam = reinterpret_cast<LPARAM>(&ncParams); 
      newMsg.Result = 0; 
      this->TFormStyleHook::WndProc(newMsg); 

      if (this->Handled) { 
       *reinterpret_cast<RECT*>(Message.LParam) = ncParams.rgrc[0]; 
       Message.Result = 0; 
      } 
     } else { 
      this->TFormStyleHook::WndProc(Message); 
     } 
    } 
}; 
//--------------------------------------------------------------------------- 
int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) 
{ 
    // Register our style hook. An audit of C++ Builder XE8 VCL source code 
    // for registration of the existing TFormStyleHook shows that these are 
    // the only two classes we need to register for. 
    TCustomStyleEngine::RegisterStyleHook(__classid(TForm), 
     __classid(TFixedFormStyleHook)); 
    TCustomStyleEngine::RegisterStyleHook(__classid(TCustomForm), 
     __classid(TFixedFormStyleHook)); 

    Application->Initialize(); 
    Application->MainFormOnTaskBar = true; 
    TStyleManager::TrySetStyle("Carbon"); 
    Application->CreateForm(__classid(TForm1), &Form1); 
    Application->Run(); 
    return 0; 
} 
//--------------------------------------------------------------------------- 

今すぐプロジェクトを実行し、ボタンをクリックしてください。 WM_NCCALCSIZEが正しく処理されるようになります。また、DFMファイルに明示的にClientWidthを設定すると、正しく使用されることがわかります。

関連する問題