現在位置: ホーム / TinBlog / SystemParameters.ResizeFrameVerticalBorderWidth が完全役立たずな件

SystemParameters.ResizeFrameVerticalBorderWidth が完全役立たずな件

作者: h2 最終変更日時 2014年02月03日 05時45分 |
カテゴリー:
諸々あって、C#でアプリ製作をするハメになった。登場当初の悪夢的出来の悪さを目の当たりにして、C#は避けてきたんだけどね。そうも言っていられない案件に出くわしてしまって、しぶしぶ。

まあ、細かな事情はさておき、数年ぶりに改めてC#を眺め回してみると、そこそこ使えるレベルになりつつあるのに感心した。動作がとろかった点も、随分と改善されているようだし。まだ未確認だけど、以前あったワークメモリのショボ過ぎる上限が撤廃されているようなら、よっしゃ、なんだけどね。

 

それはさておき、開発中のアプリケーションで、ウインドウの最小サイズをコントロールする必要が出てきたんですよ。よくある話だけど。

WPFで組んでいるんだけど、xmlの指定だけだと、クライアントサイズを基準としたMinWidth/MinHeightの設定に難が出てしまったので、やむを得ず、この部分はHogeWindow.csにコーディングすることにした・・・までは良かったのだけど。

 

WPFだから、SystemParameters.WindowResizeBorderThickness で簡単にウインドウの枠幅が取れるなあ、とか思っていたら、なぜか結果が微妙にずれる。ググってみても、基本的なウインドウ幅の指定は、だいたい同じようにしている記事しか見当たらない。

double winWidth = clientWidth + SystemParameters.WindowResizeBorderThickness * 2;

みたいな。

 

おかしいな〜、とかおもいつつ、更にググってみると、同じ問題で悩んでいた人を見つけた。

WPF has the SystemParameters class that exposes a great number of system metrics. On my computer I have noticed that a normal window has a title that is 30 pixels high and a border that is 8 pixels wide.

・・・(中略)

How do I compute the actual observed values by using SystemParameters?

Stack Overflow Q&A: How do I compute the non-client window size in WPF?

ただ、残念なことに、ここでの質疑応答内容も、どうもピンとこない、というか、解決策にはなっていないようなんですよ。

 

しょ〜がないので、更にググり続けること小一時間。ようやく見つけたのは、本家のマイクロソフト。

The SystemParameters.WindowResizeBorderThickness seems to return incorrect value in my WPF project built using VS2012. This property is new in .Net Framework 4.5.

Before upgrading my project to VS2012/Net4.5 I was using VS2010/Net4.0, using the WPF Shell Integration Library found here: http://archive.msdn.microsoft.com/WPFShell/Release/ProjectReleases.aspx?ReleaseId=4332

When I built my project using VS2010, the SystemParameters2.Current.WindowResizeBorderThickness property (from the library linked above) returned a value of 8 (correct on my system). When taking the same project and open it in VS2012 and rebuilding, the same property returns a value of 4 (incorrect on my system).

Microsoft Connect Feedback: The SystemParameters.WindowResizeBorderThickness seems to return incorrect value

よっしゃ、まんまの記事。・・・なんだけど。

 

かいつまんで言うと。「OSのバージョンによって値が変わるから、GetSystemMetrics(SM_CXPADDEDBORDER); してね」。

なんじゃそりゃ。関連しそうな記事も、同じくマイクロソフトのサポートにありましたが、

SystemParametersInfo 関数を使用して SPI_GETNONCLIENTMETRICS の値を取得する場合、構造体のサイズおよび実行ファイルのサブシステム指定によって取得できる値が異なります。Windows 7 の環境にてすべての値を取得するには、Windows 7 用に開発されたアプリケーションであることを示す必要があります。

・・・(中略)

なお、関数内部の処理にて NONCLIENTMETRICS 構造体の値を参照する GetSystemMetrics 関数の返り値も、この指定の影響を受けて変化します。GetSystemMetrics 関数を使用して以下の値を取得する際には、期待する値が取得できているか、注意が必要となります。

  • SM_CXFRAME
  • SM_CYFRAME
  • SM_CXPADDEDBORDER
  • ・・・(中略)

    この動作は仕様です。

    Microsoft Support: NONCLIENTMETRICS 構造体の iPaddedBorderWidth 値を正しく取得する方法

    注意が必要、じゃねーだろコラ。しかも、「この動作は仕様」ときたもんだ。

     

    そしてそして。ここからが「どびっくり」なのですが、このSM_CXPADDEDBORDERに相当する値を、SystemParametersは持っていないんですね。

    つまり。たかだか「ウインドウの枠のサイズを取得したい」だけのことなのに、くっっっそださい迂回策を取らざるを得ないのですよ。

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int GetSystemMetrics(int nIndex);
        protected void ResetActualMinSize()
        {
            const int CYCAPTION = 0x04;
            const int CXFRAME = 0x20;
            const int CYFRAME = 0x21;
            const int CXPADDEDBORDER = 92;
    
            // Gets the window frame thickness.
            Version ver = Environment.OSVersion.Version;
            int borderWidth = 0, borderHeight = 0, captionHeight = 0, paddingThckness = 0;
            if ((ver.Major > 6) || ((ver.Major == 6) && (ver.Minor >= 1)))
            {   // Windows7 or later.
                paddingThckness = GetSystemMetrics(CXPADDEDBORDER);
            }
            borderWidth = GetSystemMetrics(CXFRAME);
            borderHeight = GetSystemMetrics(CYFRAME);
            captionHeight = GetSystemMetrics(CYCAPTION);
    
            // Resets the actual min window size.
            ActualMinWidth = ClientMinWidth + (borderWidth + paddingThckness) * 2;
    
    ....

    ・・・みたいな。

     

    いちおう、Win7以降がおかしくなるような感じなので、SM_CXPADDEDBORDERを拾うのは7以降に限定しているけど、これは不要かも知れない。

    ついでに、まだ開発初期の段階なので、Win8.1+.NET4.5でしか動作確認していないけど。

     

    え〜と。MSのトンデモ仕様には、毎回のようにorzらされるんだけど。

    ここまでトホホなのは久しぶりだぞ、ちくしょ〜。

    わざわざBORDERを分けた理由が、リボンインターフェースのせいとか言う話もあったりするのが、さらに腹立つぞ。

    タグ:
    « 2019 年 2月 »
    2月
    12
    3456789
    10111213141516
    17181920212223
    2425262728